From e8f5160ce40e05753d29ced35cf9b246ef12eb2d Mon Sep 17 00:00:00 2001 From: JGoutin Date: Sun, 14 Feb 2016 20:15:36 +0000 Subject: Improve support for standalones MVC++ 9.0/10.0 compilers by setting directly the environment if Vcvarsall.bat is missing or fail. --- setuptools/msvc9_support.py | 265 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 255 insertions(+), 10 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index a69c7474..5dad8db2 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -48,16 +48,261 @@ def find_vcvarsall(version): return unpatched['find_vcvarsall'](version) def query_vcvarsall(version, *args, **kwargs): + message = '' + + # Try to get environement from vcvarsall.bat (Classical way) try: return unpatched['query_vcvarsall'](version, *args, **kwargs) except distutils.errors.DistutilsPlatformError as exc: - if exc and "vcvarsall.bat" in exc.args[0]: - message = 'Microsoft Visual C++ %0.1f is required (%s).' % (version, exc.args[0]) - if int(version) == 9: - # This redirection link is maintained by Microsoft. - # Contact vspython@microsoft.com if it needs updating. - raise distutils.errors.DistutilsPlatformError( - message + ' Get it from http://aka.ms/vcpython27' - ) - raise distutils.errors.DistutilsPlatformError(message) - raise + # Error if Vcvarsall.bat is missing + message = exc.args[0] + except ValueError as exc: + # Error if environment not set after executing vcvarsall.bat + message = exc.args[0] + + # If vcvarsall.bat fail, try to set environment directly + try: + return setvcenv(version, *args, **kwargs) + except distutils.errors.DistutilsPlatformError as exc: + # Error if MSVC++ directory not found or environment not set + message = exc.args[0] + + # Raise error + if message and "vcvarsall.bat" in message: + # Special error message if MSVC++ not installed + message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ + (version, message) + if int(version) == 9: + # For VC++ 9.0 Redirect user to Vc++ for Python 2.7 : + # This redirection link is maintained by Microsoft. + # Contact vspython@microsoft.com if it needs updating. + message += r' Get it from http://aka.ms/vcpython27' + elif int(version) == 10: + # For VC++ 10.0 Redirect user to Windows SDK 7.1 + message += ' Get it with "Microsoft Windows SDK for Windows 7": ' + message += r'www.microsoft.com/download/details.aspx?id=8279' + raise distutils.errors.DistutilsPlatformError(message) + raise(message) + +def setvcenv(VcVer, arch): + """ + Return environment variables for specified Microsoft Visual C++ version + and platform. + """ + from os.path import join, isdir + from os import environ + from distutils.errors import DistutilsPlatformError + + # Find current and target architecture + CurrentCpu = environ['processor_architecture'].lower() + TargetCpu = arch[arch.find('_') + 1:] + + # Find "Windows" and "Program Files" system directories + WinDir = environ['WinDir'] + ProgramFiles = environ['ProgramFiles'] + if CurrentCpu != 'x86': + ProgramFilesX86 = environ['ProgramFiles(x86)'] + else: + ProgramFilesX86 = ProgramFiles + + # Set registry base paths + reg_value = distutils.msvc9compiler.Reg.get_value + if CurrentCpu != 'x86': + node = r'\Wow6432Node' + else: + node = '' + VsReg = r'Software%s\Microsoft\VisualStudio\SxS\VS7' % node + VcReg = r'Software%s\Microsoft\VisualStudio\SxS\VC7' % node + VcForPythonReg = r'Software%s\Microsoft\DevDiv\VCForPython\%0.1f' %\ + (node, VcVer) + WindowsSdkReg = r'Software%s\Microsoft\Microsoft SDKs\Windows' % node + + # Set Platform subdirectories + if TargetCpu == 'amd64': + pltsd1, pltsd2 = r'\amd64', r'\x64' + if CurrentCpu == 'amd64': + pltsd3 = r'\amd64' + else: + pltsd3 = r'\x86_amd64' + elif TargetCpu == 'ia64': + pltsd1, pltsd2 = r'\ia64', r'\ia64' + if CurrentCpu == 'ia64': + pltsd3 = r'\ia64' + else: + pltsd3 = r'\x86_ia64' + else: + pltsd1, pltsd2, pltsd3 = '', '', '' + + # Find Microsoft Visual Studio directory + try: + # Try to get it from registry + VsInstallDir = reg_value(VsReg, '%0.1f' % VcVer) + except KeyError: + # If fail, use default path + VsInstallDir = join(ProgramFilesX86, + 'Microsoft Visual Studio %0.1f' % VcVer) + + # Find Microsoft Visual C++ directory + try: + # Try to get it from registry + VcInstallDir = reg_value(VcReg, '%0.1f' % VcVer) + except KeyError: + try: + # Try to get "VC++ for Python" version from registry + VcInstallDir = join(reg_value(VcForPythonReg, 'installdir'), 'VC') + except KeyError: + # If fail, use default path + VcInstallDir = join(ProgramFilesX86, + r'Microsoft Visual Studio %0.1f\VC' % VcVer) + if not isdir(VcInstallDir): + raise DistutilsPlatformError('vcvarsall.bat and Visual C++ ' + 'directory not found') + + # Find Microsoft Windows SDK directory + WindowsSdkDir = '' + if VcVer == 9.0: + WindowsSdkVer = ('7.0', '6.1', '6.0a') + elif VcVer == 10.0: + WindowsSdkVer = ('7.1', '7.0a') + else: + WindowsSdkVer = () + for ver in WindowsSdkVer: + # Try to get it from registry + try: + WindowsSdkDir = reg_value(join(WindowsSdkReg, 'v%s' % ver), + 'installationfolder') + break + except KeyError: + pass + if not WindowsSdkDir or not isdir(WindowsSdkDir): + # Try to get "VC++ for Python" version from registry + try: + WindowsSdkDir = join(reg_value(VcForPythonReg, 'installdir'), + 'WinSDK') + except: + pass + if not WindowsSdkDir or not isdir(WindowsSdkDir): + # If fail, use default path + for ver in WindowsSdkVer: + d = join(ProgramFiles, r'Microsoft SDKs\Windows\v%s' % ver) + if isdir(d): + WindowsSdkDir = d + if not WindowsSdkDir: + # If fail, use Platform SDK + WindowsSdkDir = join(VcInstallDir, 'PlatformSDK') + + # Find Microsoft .NET Framework 32bit directory + try: + # Try to get it from registry + FrameworkDir32 = reg_value(VcReg, 'frameworkdir32') + except KeyError: + # If fail, use default path + FrameworkDir32 = join(WinDir, r'Microsoft.NET\Framework') + + # Find Microsoft .NET Framework 64bit directory + try: + # Try to get it from registry + FrameworkDir64 = reg_value(VcReg, 'frameworkdir64') + except KeyError: + # If fail, use default path + FrameworkDir64 = join(WinDir, r'Microsoft.NET\Framework64') + + # Find Microsoft .NET Framework Versions + if VcVer == 10.0: + try: + # Try to get v4 from registry + v4 = reg_value(VcReg, 'frameworkver32') + if v4.lower()[:2] != 'v4': + raise KeyError('Not the V4') + except KeyError: + # If fail, use last v4 version + v4 = 'v4.0.30319' + FrameworkVer = (v4, 'v3.5') + elif VcVer == 9.0: + FrameworkVer = ('v3.5', 'v2.0.50727') + elif VcVer == 8.0: + FrameworkVer = ('v3.0', 'v2.0.50727') + + # Set Microsoft Visual Studio Tools + VSTools = [join(VsInstallDir, r'Common7\IDE'), + join(VsInstallDir, r'Common7\Tools')] + + # Set Microsoft Visual C++ Includes + VCIncludes = join(VcInstallDir, 'Include') + + # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries + VCLibraries = [join(VcInstallDir, 'Lib' + pltsd1), + join(VcInstallDir, r'ATLMFC\LIB' + pltsd1)] + + # Set Microsoft Visual C++ Tools + VCTools = [join(VcInstallDir, 'VCPackages'), + join(VcInstallDir, 'Bin' + pltsd3)] + if pltsd3: + VCTools.append(join(VcInstallDir, 'Bin')) + + # Set Microsoft Windows SDK Include + OSLibraries = join(WindowsSdkDir, 'Lib' + pltsd2) + + # Set Microsoft Windows SDK Libraries + OSIncludes = [join(WindowsSdkDir, 'Include'), + join(WindowsSdkDir, r'Include\gl')] + + # Set Microsoft Windows SDK Tools + SdkTools = [join(WindowsSdkDir, 'Bin')] + if TargetCpu != 'x86': + SdkTools.append(join(WindowsSdkDir, 'Bin' + pltsd2)) + if VcVer == 10.0: + SdkTools.append(join(WindowsSdkDir, r'Bin\NETFX 4.0 Tools' + pltsd2)) + + # Set Microsoft Windows SDK Setup + SdkSetup = join(WindowsSdkDir, 'Setup') + + # Set Microsoft .NET Framework Tools + FxTools = [] + for ver in FrameworkVer: + FxTools.append(join(FrameworkDir32, ver)) + if TargetCpu != 'x86' and CurrentCpu != 'x86': + for ver in FrameworkVer: + FxTools.append(join(FrameworkDir64, ver)) + + # Set Microsoft Visual Studio Team System Database + VsTDb = join(VsInstallDir, r'VSTSDB\Deploy') + + # Return Environment Variables + env = {} + env['include'] = [VCIncludes, OSIncludes] + env['lib'] = [VCLibraries, OSLibraries, FxTools] + env['libpath'] = [VCLibraries, FxTools] + env['path'] = [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, FxTools] + + def checkpath(path, varlist): + # Function that add valid paths in list in not already present + if isdir(path) and path not in varlist: + varlist.append(path) + + for key in env.keys(): + var = [] + # Add valid paths + for val in env[key]: + if type(val) is str: + # Path + checkpath(val, var) + else: + # Paths list + for subval in val: + checkpath(subval, var) + + # Add values from actual environment + try: + for val in environ[key].split(';'): + checkpath(val, var) + except KeyError: + pass + + # Format paths to Environment Variable string + if var: + env[key] = ';'.join(var) + else: + raise DistutilsPlatformError("%s environment variable is empty" % + key.upper()) + return env -- cgit v1.2.1 From 11e560431408ccab2ef5e35721ef61d664d13693 Mon Sep 17 00:00:00 2001 From: JGoutin Date: Thu, 18 Feb 2016 19:59:21 +0000 Subject: Update with comments. --- setuptools/msvc9_support.py | 76 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 5dad8db2..7e8a2a25 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -62,7 +62,11 @@ def query_vcvarsall(version, *args, **kwargs): # If vcvarsall.bat fail, try to set environment directly try: - return setvcenv(version, *args, **kwargs) + if not args: + arch = 'x86' + else: + arch = args[0] + return setvcenv(version, kwargs.get('arch', arch)) except distutils.errors.DistutilsPlatformError as exc: # Error if MSVC++ directory not found or environment not set message = exc.args[0] @@ -82,7 +86,7 @@ def query_vcvarsall(version, *args, **kwargs): message += ' Get it with "Microsoft Windows SDK for Windows 7": ' message += r'www.microsoft.com/download/details.aspx?id=8279' raise distutils.errors.DistutilsPlatformError(message) - raise(message) + raise distutils.errors.DistutilsPlatformError(message) def setvcenv(VcVer, arch): """ @@ -96,21 +100,17 @@ def setvcenv(VcVer, arch): # Find current and target architecture CurrentCpu = environ['processor_architecture'].lower() TargetCpu = arch[arch.find('_') + 1:] + Tar_not_x86 = TargetCpu != 'x86' + Cur_not_x86 = CurrentCpu != 'x86' # Find "Windows" and "Program Files" system directories WinDir = environ['WinDir'] ProgramFiles = environ['ProgramFiles'] - if CurrentCpu != 'x86': - ProgramFilesX86 = environ['ProgramFiles(x86)'] - else: - ProgramFilesX86 = ProgramFiles + ProgramFilesX86 = environ.get('ProgramFiles(x86)', ProgramFiles) # Set registry base paths reg_value = distutils.msvc9compiler.Reg.get_value - if CurrentCpu != 'x86': - node = r'\Wow6432Node' - else: - node = '' + node = r'\Wow6432Node' if Cur_not_x86 else '' VsReg = r'Software%s\Microsoft\VisualStudio\SxS\VS7' % node VcReg = r'Software%s\Microsoft\VisualStudio\SxS\VC7' % node VcForPythonReg = r'Software%s\Microsoft\DevDiv\VCForPython\%0.1f' %\ @@ -119,19 +119,23 @@ def setvcenv(VcVer, arch): # Set Platform subdirectories if TargetCpu == 'amd64': - pltsd1, pltsd2 = r'\amd64', r'\x64' + plt_subd_lib = r'\amd64' + plt_subd_sdk = r'\x64' if CurrentCpu == 'amd64': - pltsd3 = r'\amd64' + plt_subd_tools = r'\amd64' else: - pltsd3 = r'\x86_amd64' + plt_subd_tools = r'\x86_amd64' elif TargetCpu == 'ia64': - pltsd1, pltsd2 = r'\ia64', r'\ia64' + plt_subd_lib = r'\ia64' + plt_subd_sdk = r'\ia64' if CurrentCpu == 'ia64': - pltsd3 = r'\ia64' + plt_subd_tools = r'\ia64' else: - pltsd3 = r'\x86_ia64' + plt_subd_tools = r'\x86_ia64' else: - pltsd1, pltsd2, pltsd3 = '', '', '' + plt_subd_lib = '' + plt_subd_sdk = '' + plt_subd_tools = '' # Find Microsoft Visual Studio directory try: @@ -228,20 +232,20 @@ def setvcenv(VcVer, arch): join(VsInstallDir, r'Common7\Tools')] # Set Microsoft Visual C++ Includes - VCIncludes = join(VcInstallDir, 'Include') + VCIncludes = [join(VcInstallDir, 'Include')] # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries - VCLibraries = [join(VcInstallDir, 'Lib' + pltsd1), - join(VcInstallDir, r'ATLMFC\LIB' + pltsd1)] + VCLibraries = [join(VcInstallDir, 'Lib' + plt_subd_lib), + join(VcInstallDir, r'ATLMFC\LIB' + plt_subd_lib)] # Set Microsoft Visual C++ Tools VCTools = [join(VcInstallDir, 'VCPackages'), - join(VcInstallDir, 'Bin' + pltsd3)] - if pltsd3: + join(VcInstallDir, 'Bin' + plt_subd_tools)] + if plt_subd_tools: VCTools.append(join(VcInstallDir, 'Bin')) # Set Microsoft Windows SDK Include - OSLibraries = join(WindowsSdkDir, 'Lib' + pltsd2) + OSLibraries = [join(WindowsSdkDir, 'Lib' + plt_subd_sdk)] # Set Microsoft Windows SDK Libraries OSIncludes = [join(WindowsSdkDir, 'Include'), @@ -249,24 +253,23 @@ def setvcenv(VcVer, arch): # Set Microsoft Windows SDK Tools SdkTools = [join(WindowsSdkDir, 'Bin')] - if TargetCpu != 'x86': - SdkTools.append(join(WindowsSdkDir, 'Bin' + pltsd2)) + if Tar_not_x86: + SdkTools.append(join(WindowsSdkDir, 'Bin' + plt_subd_sdk)) if VcVer == 10.0: - SdkTools.append(join(WindowsSdkDir, r'Bin\NETFX 4.0 Tools' + pltsd2)) + SdkTools.append(join(WindowsSdkDir, + r'Bin\NETFX 4.0 Tools' + plt_subd_sdk)) # Set Microsoft Windows SDK Setup - SdkSetup = join(WindowsSdkDir, 'Setup') + SdkSetup = [join(WindowsSdkDir, 'Setup')] # Set Microsoft .NET Framework Tools - FxTools = [] - for ver in FrameworkVer: - FxTools.append(join(FrameworkDir32, ver)) - if TargetCpu != 'x86' and CurrentCpu != 'x86': + FxTools = [join(FrameworkDir32, ver) for ver in FrameworkVer] + if Tar_not_x86 and Cur_not_x86: for ver in FrameworkVer: FxTools.append(join(FrameworkDir64, ver)) # Set Microsoft Visual Studio Team System Database - VsTDb = join(VsInstallDir, r'VSTSDB\Deploy') + VsTDb = [join(VsInstallDir, r'VSTSDB\Deploy')] # Return Environment Variables env = {} @@ -284,13 +287,8 @@ def setvcenv(VcVer, arch): var = [] # Add valid paths for val in env[key]: - if type(val) is str: - # Path - checkpath(val, var) - else: - # Paths list - for subval in val: - checkpath(subval, var) + for subval in val: + checkpath(subval, var) # Add values from actual environment try: -- cgit v1.2.1 From 4f85ab4276a862467a927e2d1603e063728ba647 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Feb 2016 10:18:31 -0500 Subject: Update changelog --- CHANGES.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 75b13e46..9209a1db 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,12 @@ CHANGES ======= +Next +---- + +* Pull Request #174: Add more aggressive support for + Windows SDK in msvc9compiler patch. + 20.1.1 ------ -- cgit v1.2.1 From b165a8bec12ec7a7cf606ceb83e94e80f22445dc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 11:01:28 -0400 Subject: Rename variable for consistency --- setuptools/msvc9_support.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 7e8a2a25..2524ec41 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -88,7 +88,7 @@ def query_vcvarsall(version, *args, **kwargs): raise distutils.errors.DistutilsPlatformError(message) raise distutils.errors.DistutilsPlatformError(message) -def setvcenv(VcVer, arch): +def setvcenv(version, arch): """ Return environment variables for specified Microsoft Visual C++ version and platform. @@ -114,7 +114,7 @@ def setvcenv(VcVer, arch): VsReg = r'Software%s\Microsoft\VisualStudio\SxS\VS7' % node VcReg = r'Software%s\Microsoft\VisualStudio\SxS\VC7' % node VcForPythonReg = r'Software%s\Microsoft\DevDiv\VCForPython\%0.1f' %\ - (node, VcVer) + (node, version) WindowsSdkReg = r'Software%s\Microsoft\Microsoft SDKs\Windows' % node # Set Platform subdirectories @@ -140,16 +140,16 @@ def setvcenv(VcVer, arch): # Find Microsoft Visual Studio directory try: # Try to get it from registry - VsInstallDir = reg_value(VsReg, '%0.1f' % VcVer) + VsInstallDir = reg_value(VsReg, '%0.1f' % version) except KeyError: # If fail, use default path VsInstallDir = join(ProgramFilesX86, - 'Microsoft Visual Studio %0.1f' % VcVer) + 'Microsoft Visual Studio %0.1f' % version) # Find Microsoft Visual C++ directory try: # Try to get it from registry - VcInstallDir = reg_value(VcReg, '%0.1f' % VcVer) + VcInstallDir = reg_value(VcReg, '%0.1f' % version) except KeyError: try: # Try to get "VC++ for Python" version from registry @@ -157,16 +157,16 @@ def setvcenv(VcVer, arch): except KeyError: # If fail, use default path VcInstallDir = join(ProgramFilesX86, - r'Microsoft Visual Studio %0.1f\VC' % VcVer) + r'Microsoft Visual Studio %0.1f\VC' % version) if not isdir(VcInstallDir): raise DistutilsPlatformError('vcvarsall.bat and Visual C++ ' 'directory not found') # Find Microsoft Windows SDK directory WindowsSdkDir = '' - if VcVer == 9.0: + if version == 9.0: WindowsSdkVer = ('7.0', '6.1', '6.0a') - elif VcVer == 10.0: + elif version == 10.0: WindowsSdkVer = ('7.1', '7.0a') else: WindowsSdkVer = () @@ -212,7 +212,7 @@ def setvcenv(VcVer, arch): FrameworkDir64 = join(WinDir, r'Microsoft.NET\Framework64') # Find Microsoft .NET Framework Versions - if VcVer == 10.0: + if version == 10.0: try: # Try to get v4 from registry v4 = reg_value(VcReg, 'frameworkver32') @@ -222,9 +222,9 @@ def setvcenv(VcVer, arch): # If fail, use last v4 version v4 = 'v4.0.30319' FrameworkVer = (v4, 'v3.5') - elif VcVer == 9.0: + elif version == 9.0: FrameworkVer = ('v3.5', 'v2.0.50727') - elif VcVer == 8.0: + elif version == 8.0: FrameworkVer = ('v3.0', 'v2.0.50727') # Set Microsoft Visual Studio Tools @@ -255,7 +255,7 @@ def setvcenv(VcVer, arch): SdkTools = [join(WindowsSdkDir, 'Bin')] if Tar_not_x86: SdkTools.append(join(WindowsSdkDir, 'Bin' + plt_subd_sdk)) - if VcVer == 10.0: + if version == 10.0: SdkTools.append(join(WindowsSdkDir, r'Bin\NETFX 4.0 Tools' + plt_subd_sdk)) -- cgit v1.2.1 From 17379ffe70c7d6c8c7de65013c91bc20c7b8b274 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 11:10:47 -0400 Subject: Include 'arch' in the function signature rather than processing arguments by hand. --- setuptools/msvc9_support.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 2524ec41..fc8102c9 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -47,12 +47,12 @@ def find_vcvarsall(version): return unpatched['find_vcvarsall'](version) -def query_vcvarsall(version, *args, **kwargs): +def query_vcvarsall(version, arch='x86', *args, **kwargs): message = '' # Try to get environement from vcvarsall.bat (Classical way) try: - return unpatched['query_vcvarsall'](version, *args, **kwargs) + return unpatched['query_vcvarsall'](version, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError as exc: # Error if Vcvarsall.bat is missing message = exc.args[0] @@ -62,11 +62,7 @@ def query_vcvarsall(version, *args, **kwargs): # If vcvarsall.bat fail, try to set environment directly try: - if not args: - arch = 'x86' - else: - arch = args[0] - return setvcenv(version, kwargs.get('arch', arch)) + return setvcenv(version, arch) except distutils.errors.DistutilsPlatformError as exc: # Error if MSVC++ directory not found or environment not set message = exc.args[0] -- cgit v1.2.1 From 21480cfc6fbe1da481698484549ecb490f098994 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 11:14:33 -0400 Subject: Make function private and don't use a different name for it, as it performs the same purpose. Also, it doesn't set anything. --- setuptools/msvc9_support.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index fc8102c9..3b5133c8 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -62,7 +62,7 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): # If vcvarsall.bat fail, try to set environment directly try: - return setvcenv(version, arch) + return _query_vcvarsall(version, arch) except distutils.errors.DistutilsPlatformError as exc: # Error if MSVC++ directory not found or environment not set message = exc.args[0] @@ -84,7 +84,7 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): raise distutils.errors.DistutilsPlatformError(message) raise distutils.errors.DistutilsPlatformError(message) -def setvcenv(version, arch): +def _query_vcvarsall(version, arch): """ Return environment variables for specified Microsoft Visual C++ version and platform. -- cgit v1.2.1 From 36ec20b295897ff953908af0fd2791e56ae3d0b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 13:41:06 -0400 Subject: Move imports to the top and normalize usage. --- setuptools/msvc9_support.py | 90 ++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 3b5133c8..2d5d2a84 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -1,3 +1,6 @@ +import os +import distutils.errors + try: import distutils.msvc9compiler except ImportError: @@ -40,8 +43,7 @@ def find_vcvarsall(version): productdir = None if productdir: - import os - vcvarsall = os.path.join(productdir, "vcvarsall.bat") + vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat") if os.path.isfile(vcvarsall): return vcvarsall @@ -89,20 +91,16 @@ def _query_vcvarsall(version, arch): Return environment variables for specified Microsoft Visual C++ version and platform. """ - from os.path import join, isdir - from os import environ - from distutils.errors import DistutilsPlatformError - # Find current and target architecture - CurrentCpu = environ['processor_architecture'].lower() + CurrentCpu = os.environ['processor_architecture'].lower() TargetCpu = arch[arch.find('_') + 1:] Tar_not_x86 = TargetCpu != 'x86' Cur_not_x86 = CurrentCpu != 'x86' # Find "Windows" and "Program Files" system directories - WinDir = environ['WinDir'] - ProgramFiles = environ['ProgramFiles'] - ProgramFilesX86 = environ.get('ProgramFiles(x86)', ProgramFiles) + WinDir = os.environ['WinDir'] + ProgramFiles = os.environ['ProgramFiles'] + ProgramFilesX86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) # Set registry base paths reg_value = distutils.msvc9compiler.Reg.get_value @@ -139,7 +137,7 @@ def _query_vcvarsall(version, arch): VsInstallDir = reg_value(VsReg, '%0.1f' % version) except KeyError: # If fail, use default path - VsInstallDir = join(ProgramFilesX86, + VsInstallDir = os.path.join(ProgramFilesX86, 'Microsoft Visual Studio %0.1f' % version) # Find Microsoft Visual C++ directory @@ -149,13 +147,13 @@ def _query_vcvarsall(version, arch): except KeyError: try: # Try to get "VC++ for Python" version from registry - VcInstallDir = join(reg_value(VcForPythonReg, 'installdir'), 'VC') + VcInstallDir = os.path.join(reg_value(VcForPythonReg, 'installdir'), 'VC') except KeyError: # If fail, use default path - VcInstallDir = join(ProgramFilesX86, + VcInstallDir = os.path.join(ProgramFilesX86, r'Microsoft Visual Studio %0.1f\VC' % version) - if not isdir(VcInstallDir): - raise DistutilsPlatformError('vcvarsall.bat and Visual C++ ' + if not os.path.isdir(VcInstallDir): + raise distutils.errors.DistutilsPlatformError('vcvarsall.bat and Visual C++ ' 'directory not found') # Find Microsoft Windows SDK directory @@ -169,27 +167,27 @@ def _query_vcvarsall(version, arch): for ver in WindowsSdkVer: # Try to get it from registry try: - WindowsSdkDir = reg_value(join(WindowsSdkReg, 'v%s' % ver), + WindowsSdkDir = reg_value(os.path.join(WindowsSdkReg, 'v%s' % ver), 'installationfolder') break except KeyError: pass - if not WindowsSdkDir or not isdir(WindowsSdkDir): + if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # Try to get "VC++ for Python" version from registry try: - WindowsSdkDir = join(reg_value(VcForPythonReg, 'installdir'), + WindowsSdkDir = os.path.join(reg_value(VcForPythonReg, 'installdir'), 'WinSDK') except: pass - if not WindowsSdkDir or not isdir(WindowsSdkDir): + if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # If fail, use default path for ver in WindowsSdkVer: - d = join(ProgramFiles, r'Microsoft SDKs\Windows\v%s' % ver) - if isdir(d): + d = os.path.join(ProgramFiles, r'Microsoft SDKs\Windows\v%s' % ver) + if os.path.isdir(d): WindowsSdkDir = d if not WindowsSdkDir: # If fail, use Platform SDK - WindowsSdkDir = join(VcInstallDir, 'PlatformSDK') + WindowsSdkDir = os.path.join(VcInstallDir, 'PlatformSDK') # Find Microsoft .NET Framework 32bit directory try: @@ -197,7 +195,7 @@ def _query_vcvarsall(version, arch): FrameworkDir32 = reg_value(VcReg, 'frameworkdir32') except KeyError: # If fail, use default path - FrameworkDir32 = join(WinDir, r'Microsoft.NET\Framework') + FrameworkDir32 = os.path.join(WinDir, r'Microsoft.NET\Framework') # Find Microsoft .NET Framework 64bit directory try: @@ -205,7 +203,7 @@ def _query_vcvarsall(version, arch): FrameworkDir64 = reg_value(VcReg, 'frameworkdir64') except KeyError: # If fail, use default path - FrameworkDir64 = join(WinDir, r'Microsoft.NET\Framework64') + FrameworkDir64 = os.path.join(WinDir, r'Microsoft.NET\Framework64') # Find Microsoft .NET Framework Versions if version == 10.0: @@ -224,48 +222,48 @@ def _query_vcvarsall(version, arch): FrameworkVer = ('v3.0', 'v2.0.50727') # Set Microsoft Visual Studio Tools - VSTools = [join(VsInstallDir, r'Common7\IDE'), - join(VsInstallDir, r'Common7\Tools')] + VSTools = [os.path.join(VsInstallDir, r'Common7\IDE'), + os.path.join(VsInstallDir, r'Common7\Tools')] # Set Microsoft Visual C++ Includes - VCIncludes = [join(VcInstallDir, 'Include')] + VCIncludes = [os.path.join(VcInstallDir, 'Include')] # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries - VCLibraries = [join(VcInstallDir, 'Lib' + plt_subd_lib), - join(VcInstallDir, r'ATLMFC\LIB' + plt_subd_lib)] + VCLibraries = [os.path.join(VcInstallDir, 'Lib' + plt_subd_lib), + os.path.join(VcInstallDir, r'ATLMFC\LIB' + plt_subd_lib)] # Set Microsoft Visual C++ Tools - VCTools = [join(VcInstallDir, 'VCPackages'), - join(VcInstallDir, 'Bin' + plt_subd_tools)] + VCTools = [os.path.join(VcInstallDir, 'VCPackages'), + os.path.join(VcInstallDir, 'Bin' + plt_subd_tools)] if plt_subd_tools: - VCTools.append(join(VcInstallDir, 'Bin')) + VCTools.append(os.path.join(VcInstallDir, 'Bin')) # Set Microsoft Windows SDK Include - OSLibraries = [join(WindowsSdkDir, 'Lib' + plt_subd_sdk)] + OSLibraries = [os.path.join(WindowsSdkDir, 'Lib' + plt_subd_sdk)] # Set Microsoft Windows SDK Libraries - OSIncludes = [join(WindowsSdkDir, 'Include'), - join(WindowsSdkDir, r'Include\gl')] + OSIncludes = [os.path.join(WindowsSdkDir, 'Include'), + os.path.join(WindowsSdkDir, r'Include\gl')] # Set Microsoft Windows SDK Tools - SdkTools = [join(WindowsSdkDir, 'Bin')] + SdkTools = [os.path.join(WindowsSdkDir, 'Bin')] if Tar_not_x86: - SdkTools.append(join(WindowsSdkDir, 'Bin' + plt_subd_sdk)) + SdkTools.append(os.path.join(WindowsSdkDir, 'Bin' + plt_subd_sdk)) if version == 10.0: - SdkTools.append(join(WindowsSdkDir, + SdkTools.append(os.path.join(WindowsSdkDir, r'Bin\NETFX 4.0 Tools' + plt_subd_sdk)) # Set Microsoft Windows SDK Setup - SdkSetup = [join(WindowsSdkDir, 'Setup')] + SdkSetup = [os.path.join(WindowsSdkDir, 'Setup')] # Set Microsoft .NET Framework Tools - FxTools = [join(FrameworkDir32, ver) for ver in FrameworkVer] + FxTools = [os.path.join(FrameworkDir32, ver) for ver in FrameworkVer] if Tar_not_x86 and Cur_not_x86: for ver in FrameworkVer: - FxTools.append(join(FrameworkDir64, ver)) + FxTools.append(os.path.join(FrameworkDir64, ver)) # Set Microsoft Visual Studio Team System Database - VsTDb = [join(VsInstallDir, r'VSTSDB\Deploy')] + VsTDb = [os.path.join(VsInstallDir, r'VSTSDB\Deploy')] # Return Environment Variables env = {} @@ -276,7 +274,7 @@ def _query_vcvarsall(version, arch): def checkpath(path, varlist): # Function that add valid paths in list in not already present - if isdir(path) and path not in varlist: + if os.path.isdir(path) and path not in varlist: varlist.append(path) for key in env.keys(): @@ -288,15 +286,15 @@ def _query_vcvarsall(version, arch): # Add values from actual environment try: - for val in environ[key].split(';'): + for val in os.environ[key].split(';'): checkpath(val, var) except KeyError: pass # Format paths to Environment Variable string if var: - env[key] = ';'.join(var) + env[key] = ';'.os.path.join(var) else: - raise DistutilsPlatformError("%s environment variable is empty" % + raise distutils.errors.DistutilsPlatformError("%s environment variable is empty" % key.upper()) return env -- cgit v1.2.1 From d773b79ae2143f18c941179f1b4e39a3e5c460af Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 13:50:28 -0400 Subject: Reindent to eliminate long lines and hanging indents. --- setuptools/msvc9_support.py | 52 ++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 2d5d2a84..095957fa 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -137,8 +137,8 @@ def _query_vcvarsall(version, arch): VsInstallDir = reg_value(VsReg, '%0.1f' % version) except KeyError: # If fail, use default path - VsInstallDir = os.path.join(ProgramFilesX86, - 'Microsoft Visual Studio %0.1f' % version) + name = 'Microsoft Visual Studio %0.1f' % version + VsInstallDir = os.path.join(ProgramFilesX86, name) # Find Microsoft Visual C++ directory try: @@ -147,14 +147,15 @@ def _query_vcvarsall(version, arch): except KeyError: try: # Try to get "VC++ for Python" version from registry - VcInstallDir = os.path.join(reg_value(VcForPythonReg, 'installdir'), 'VC') + install_base = reg_value(VcForPythonReg, 'installdir') + VcInstallDir = os.path.join(install_base, 'VC') except KeyError: # If fail, use default path - VcInstallDir = os.path.join(ProgramFilesX86, - r'Microsoft Visual Studio %0.1f\VC' % version) + default = r'Microsoft Visual Studio %0.1f\VC' % version + VcInstallDir = os.path.join(ProgramFilesX86, default) if not os.path.isdir(VcInstallDir): - raise distutils.errors.DistutilsPlatformError('vcvarsall.bat and Visual C++ ' - 'directory not found') + msg = 'vcvarsall.bat and Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) # Find Microsoft Windows SDK directory WindowsSdkDir = '' @@ -167,22 +168,23 @@ def _query_vcvarsall(version, arch): for ver in WindowsSdkVer: # Try to get it from registry try: - WindowsSdkDir = reg_value(os.path.join(WindowsSdkReg, 'v%s' % ver), - 'installationfolder') + loc = os.path.join(WindowsSdkReg, 'v%s' % ver) + WindowsSdkDir = reg_value(loc, 'installationfolder') break except KeyError: pass if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # Try to get "VC++ for Python" version from registry try: - WindowsSdkDir = os.path.join(reg_value(VcForPythonReg, 'installdir'), - 'WinSDK') + install_base = reg_value(VcForPythonReg, 'installdir') + WindowsSdkDir = os.path.join(install_base, 'WinSDK') except: pass if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # If fail, use default path for ver in WindowsSdkVer: - d = os.path.join(ProgramFiles, r'Microsoft SDKs\Windows\v%s' % ver) + path = r'Microsoft SDKs\Windows\v%s' % ver + d = os.path.join(ProgramFiles, path) if os.path.isdir(d): WindowsSdkDir = d if not WindowsSdkDir: @@ -229,12 +231,16 @@ def _query_vcvarsall(version, arch): VCIncludes = [os.path.join(VcInstallDir, 'Include')] # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries - VCLibraries = [os.path.join(VcInstallDir, 'Lib' + plt_subd_lib), - os.path.join(VcInstallDir, r'ATLMFC\LIB' + plt_subd_lib)] + VCLibraries = [ + os.path.join(VcInstallDir, 'Lib' + plt_subd_lib), + os.path.join(VcInstallDir, r'ATLMFC\LIB' + plt_subd_lib), + ] # Set Microsoft Visual C++ Tools - VCTools = [os.path.join(VcInstallDir, 'VCPackages'), - os.path.join(VcInstallDir, 'Bin' + plt_subd_tools)] + VCTools = [ + os.path.join(VcInstallDir, 'VCPackages'), + os.path.join(VcInstallDir, 'Bin' + plt_subd_tools), + ] if plt_subd_tools: VCTools.append(os.path.join(VcInstallDir, 'Bin')) @@ -242,16 +248,18 @@ def _query_vcvarsall(version, arch): OSLibraries = [os.path.join(WindowsSdkDir, 'Lib' + plt_subd_sdk)] # Set Microsoft Windows SDK Libraries - OSIncludes = [os.path.join(WindowsSdkDir, 'Include'), - os.path.join(WindowsSdkDir, r'Include\gl')] + OSIncludes = [ + os.path.join(WindowsSdkDir, 'Include'), + os.path.join(WindowsSdkDir, r'Include\gl'), + ] # Set Microsoft Windows SDK Tools SdkTools = [os.path.join(WindowsSdkDir, 'Bin')] if Tar_not_x86: SdkTools.append(os.path.join(WindowsSdkDir, 'Bin' + plt_subd_sdk)) if version == 10.0: - SdkTools.append(os.path.join(WindowsSdkDir, - r'Bin\NETFX 4.0 Tools' + plt_subd_sdk)) + path = r'Bin\NETFX 4.0 Tools' + plt_subd_sdk + SdkTools.append(os.path.join(WindowsSdkDir, path)) # Set Microsoft Windows SDK Setup SdkSetup = [os.path.join(WindowsSdkDir, 'Setup')] @@ -295,6 +303,6 @@ def _query_vcvarsall(version, arch): if var: env[key] = ';'.os.path.join(var) else: - raise distutils.errors.DistutilsPlatformError("%s environment variable is empty" % - key.upper()) + msg = "%s environment variable is empty" % key.upper() + raise distutils.errors.DistutilsPlatformError(msg) return env -- cgit v1.2.1 From ad0bd62b505fedcfb0b18585bc8a9a3e55346c55 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 13:51:23 -0400 Subject: Remove redundant call --- setuptools/msvc9_support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 095957fa..44503383 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -83,7 +83,7 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK for Windows 7": ' message += r'www.microsoft.com/download/details.aspx?id=8279' - raise distutils.errors.DistutilsPlatformError(message) + raise distutils.errors.DistutilsPlatformError(message) def _query_vcvarsall(version, arch): -- cgit v1.2.1 From 3a122756eb4a75048e3778627bac6dd3590fbe5c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 15:04:10 -0400 Subject: Extract PlatformInfo class for capturing some of the platform info calculations. --- setuptools/msvc9_support.py | 87 +++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 44503383..2ef94ada 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -86,16 +86,53 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): raise distutils.errors.DistutilsPlatformError(message) + +class PlatformInfo: + current_cpu = os.environ['processor_architecture'].lower() + + def __init__(self, arch): + self.arch = arch + + @property + def target_cpu(self): + return self.arch[self.arch.find('_') + 1:] + + def target_is_x86(self): + return self.target_cpu == 'x86' + + def current_is_x86(self): + return self.current_cpu != 'x86' + + @property + def lib_extra(self): + return ( + r'\amd64' if self.target_cpu == 'amd64' else + r'\ia64' if self.target_cpu == 'ia64' else + '' + ) + + @property + def sdk_extra(self): + return ( + r'\x64' if self.target_cpu == 'amd64' else + r'\ia64' if self.target_cpu == 'ia64' else + '' + ) + + @property + def tools_extra(self): + path = self.lib_extra + if self.target_cpu != self.current_cpu: + path = path.replace('\\', '\\x86_') + return path + + def _query_vcvarsall(version, arch): """ Return environment variables for specified Microsoft Visual C++ version and platform. """ - # Find current and target architecture - CurrentCpu = os.environ['processor_architecture'].lower() - TargetCpu = arch[arch.find('_') + 1:] - Tar_not_x86 = TargetCpu != 'x86' - Cur_not_x86 = CurrentCpu != 'x86' + pi = PlatformInfo(arch) # Find "Windows" and "Program Files" system directories WinDir = os.environ['WinDir'] @@ -104,33 +141,13 @@ def _query_vcvarsall(version, arch): # Set registry base paths reg_value = distutils.msvc9compiler.Reg.get_value - node = r'\Wow6432Node' if Cur_not_x86 else '' + node = r'\Wow6432Node' if not pi.current_is_x86() else '' VsReg = r'Software%s\Microsoft\VisualStudio\SxS\VS7' % node VcReg = r'Software%s\Microsoft\VisualStudio\SxS\VC7' % node VcForPythonReg = r'Software%s\Microsoft\DevDiv\VCForPython\%0.1f' %\ (node, version) WindowsSdkReg = r'Software%s\Microsoft\Microsoft SDKs\Windows' % node - # Set Platform subdirectories - if TargetCpu == 'amd64': - plt_subd_lib = r'\amd64' - plt_subd_sdk = r'\x64' - if CurrentCpu == 'amd64': - plt_subd_tools = r'\amd64' - else: - plt_subd_tools = r'\x86_amd64' - elif TargetCpu == 'ia64': - plt_subd_lib = r'\ia64' - plt_subd_sdk = r'\ia64' - if CurrentCpu == 'ia64': - plt_subd_tools = r'\ia64' - else: - plt_subd_tools = r'\x86_ia64' - else: - plt_subd_lib = '' - plt_subd_sdk = '' - plt_subd_tools = '' - # Find Microsoft Visual Studio directory try: # Try to get it from registry @@ -232,20 +249,20 @@ def _query_vcvarsall(version, arch): # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries VCLibraries = [ - os.path.join(VcInstallDir, 'Lib' + plt_subd_lib), - os.path.join(VcInstallDir, r'ATLMFC\LIB' + plt_subd_lib), + os.path.join(VcInstallDir, 'Lib' + pi.lib_extra), + os.path.join(VcInstallDir, r'ATLMFC\LIB' + pi.lib_extra), ] # Set Microsoft Visual C++ Tools VCTools = [ os.path.join(VcInstallDir, 'VCPackages'), - os.path.join(VcInstallDir, 'Bin' + plt_subd_tools), + os.path.join(VcInstallDir, 'Bin' + pi.tools_extra), ] - if plt_subd_tools: + if pi.tools_extra: VCTools.append(os.path.join(VcInstallDir, 'Bin')) # Set Microsoft Windows SDK Include - OSLibraries = [os.path.join(WindowsSdkDir, 'Lib' + plt_subd_sdk)] + OSLibraries = [os.path.join(WindowsSdkDir, 'Lib' + pi.sdk_extra)] # Set Microsoft Windows SDK Libraries OSIncludes = [ @@ -255,10 +272,10 @@ def _query_vcvarsall(version, arch): # Set Microsoft Windows SDK Tools SdkTools = [os.path.join(WindowsSdkDir, 'Bin')] - if Tar_not_x86: - SdkTools.append(os.path.join(WindowsSdkDir, 'Bin' + plt_subd_sdk)) + if not pi.target_is_x86(): + SdkTools.append(os.path.join(WindowsSdkDir, 'Bin' + pi.sdk_extra)) if version == 10.0: - path = r'Bin\NETFX 4.0 Tools' + plt_subd_sdk + path = r'Bin\NETFX 4.0 Tools' + pi.sdk_extra SdkTools.append(os.path.join(WindowsSdkDir, path)) # Set Microsoft Windows SDK Setup @@ -266,7 +283,7 @@ def _query_vcvarsall(version, arch): # Set Microsoft .NET Framework Tools FxTools = [os.path.join(FrameworkDir32, ver) for ver in FrameworkVer] - if Tar_not_x86 and Cur_not_x86: + if not pi.target_is_x86() and not pi.current_is_x86(): for ver in FrameworkVer: FxTools.append(os.path.join(FrameworkDir64, ver)) -- cgit v1.2.1 From 9919fdfa31b19a1f9741dba6ba5ee94c0a4cbbae Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 15:08:39 -0400 Subject: Pull program files and win dir resolution into PlatformInfo --- setuptools/msvc9_support.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 2ef94ada..d1b7b11f 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -89,6 +89,9 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): class PlatformInfo: current_cpu = os.environ['processor_architecture'].lower() + win_dir = os.environ['WinDir'] + program_files = os.environ['ProgramFiles'] + program_files_x86 = os.environ.get('ProgramFiles(x86)', program_files) def __init__(self, arch): self.arch = arch @@ -134,11 +137,6 @@ def _query_vcvarsall(version, arch): """ pi = PlatformInfo(arch) - # Find "Windows" and "Program Files" system directories - WinDir = os.environ['WinDir'] - ProgramFiles = os.environ['ProgramFiles'] - ProgramFilesX86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) - # Set registry base paths reg_value = distutils.msvc9compiler.Reg.get_value node = r'\Wow6432Node' if not pi.current_is_x86() else '' @@ -155,7 +153,7 @@ def _query_vcvarsall(version, arch): except KeyError: # If fail, use default path name = 'Microsoft Visual Studio %0.1f' % version - VsInstallDir = os.path.join(ProgramFilesX86, name) + VsInstallDir = os.path.join(pi.program_files_x86, name) # Find Microsoft Visual C++ directory try: @@ -169,7 +167,7 @@ def _query_vcvarsall(version, arch): except KeyError: # If fail, use default path default = r'Microsoft Visual Studio %0.1f\VC' % version - VcInstallDir = os.path.join(ProgramFilesX86, default) + VcInstallDir = os.path.join(pi.program_files_x86, default) if not os.path.isdir(VcInstallDir): msg = 'vcvarsall.bat and Visual C++ directory not found' raise distutils.errors.DistutilsPlatformError(msg) @@ -201,7 +199,7 @@ def _query_vcvarsall(version, arch): # If fail, use default path for ver in WindowsSdkVer: path = r'Microsoft SDKs\Windows\v%s' % ver - d = os.path.join(ProgramFiles, path) + d = os.path.join(pi.program_files, path) if os.path.isdir(d): WindowsSdkDir = d if not WindowsSdkDir: @@ -214,7 +212,7 @@ def _query_vcvarsall(version, arch): FrameworkDir32 = reg_value(VcReg, 'frameworkdir32') except KeyError: # If fail, use default path - FrameworkDir32 = os.path.join(WinDir, r'Microsoft.NET\Framework') + FrameworkDir32 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') # Find Microsoft .NET Framework 64bit directory try: @@ -222,7 +220,7 @@ def _query_vcvarsall(version, arch): FrameworkDir64 = reg_value(VcReg, 'frameworkdir64') except KeyError: # If fail, use default path - FrameworkDir64 = os.path.join(WinDir, r'Microsoft.NET\Framework64') + FrameworkDir64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') # Find Microsoft .NET Framework Versions if version == 10.0: -- cgit v1.2.1 From 6b07888982b459b98ef358e35f5cd28bd9e720f9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 15:34:29 -0400 Subject: Extract resolution of registry paths into a RegistryInfo object. --- setuptools/msvc9_support.py | 65 +++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index d1b7b11f..aac6ca12 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -130,26 +130,57 @@ class PlatformInfo: return path +class RegistryInfo: + def __init__(self, platform_info, version): + self.platform_info = platform_info + self.version = version + + @property + def microsoft(self): + return os.path.join( + 'Software', + '' if self.platform_info.current_is_x86() else 'Wow6432Node', + 'Microsoft', + ) + + @property + def sxs(self): + return os.path.join(self.microsoft, r'VisualStudio\SxS') + + @property + def vc(self): + return os.path.join(self.sxs, 'VC7') + + @property + def vs(self): + return os.path.join(self.sxs, 'VS7') + + @property + def vc_for_python(self): + path = r'DevDiv\VCForPython\%0.1f' % self.version + return os.path.join(self.microsoft, path) + + @property + def windows_sdk(self): + return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') + + def lookup(self, base, key): + return distutils.msvc9compiler.Reg.get_value(base, key) + + def _query_vcvarsall(version, arch): """ Return environment variables for specified Microsoft Visual C++ version and platform. """ pi = PlatformInfo(arch) - - # Set registry base paths - reg_value = distutils.msvc9compiler.Reg.get_value - node = r'\Wow6432Node' if not pi.current_is_x86() else '' - VsReg = r'Software%s\Microsoft\VisualStudio\SxS\VS7' % node - VcReg = r'Software%s\Microsoft\VisualStudio\SxS\VC7' % node - VcForPythonReg = r'Software%s\Microsoft\DevDiv\VCForPython\%0.1f' %\ - (node, version) - WindowsSdkReg = r'Software%s\Microsoft\Microsoft SDKs\Windows' % node + reg = RegistryInfo(pi, version) + reg_value = reg.lookup # Find Microsoft Visual Studio directory try: # Try to get it from registry - VsInstallDir = reg_value(VsReg, '%0.1f' % version) + VsInstallDir = reg_value(reg.vs, '%0.1f' % version) except KeyError: # If fail, use default path name = 'Microsoft Visual Studio %0.1f' % version @@ -158,11 +189,11 @@ def _query_vcvarsall(version, arch): # Find Microsoft Visual C++ directory try: # Try to get it from registry - VcInstallDir = reg_value(VcReg, '%0.1f' % version) + VcInstallDir = reg_value(reg.vc, '%0.1f' % version) except KeyError: try: # Try to get "VC++ for Python" version from registry - install_base = reg_value(VcForPythonReg, 'installdir') + install_base = reg_value(reg.vc_for_python, 'installdir') VcInstallDir = os.path.join(install_base, 'VC') except KeyError: # If fail, use default path @@ -183,7 +214,7 @@ def _query_vcvarsall(version, arch): for ver in WindowsSdkVer: # Try to get it from registry try: - loc = os.path.join(WindowsSdkReg, 'v%s' % ver) + loc = os.path.join(reg.windows_sdk, 'v%s' % ver) WindowsSdkDir = reg_value(loc, 'installationfolder') break except KeyError: @@ -191,7 +222,7 @@ def _query_vcvarsall(version, arch): if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # Try to get "VC++ for Python" version from registry try: - install_base = reg_value(VcForPythonReg, 'installdir') + install_base = reg_value(reg.vc_for_python, 'installdir') WindowsSdkDir = os.path.join(install_base, 'WinSDK') except: pass @@ -209,7 +240,7 @@ def _query_vcvarsall(version, arch): # Find Microsoft .NET Framework 32bit directory try: # Try to get it from registry - FrameworkDir32 = reg_value(VcReg, 'frameworkdir32') + FrameworkDir32 = reg_value(reg.vc, 'frameworkdir32') except KeyError: # If fail, use default path FrameworkDir32 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') @@ -217,7 +248,7 @@ def _query_vcvarsall(version, arch): # Find Microsoft .NET Framework 64bit directory try: # Try to get it from registry - FrameworkDir64 = reg_value(VcReg, 'frameworkdir64') + FrameworkDir64 = reg_value(reg.vc, 'frameworkdir64') except KeyError: # If fail, use default path FrameworkDir64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') @@ -226,7 +257,7 @@ def _query_vcvarsall(version, arch): if version == 10.0: try: # Try to get v4 from registry - v4 = reg_value(VcReg, 'frameworkver32') + v4 = reg_value(reg.vc, 'frameworkver32') if v4.lower()[:2] != 'v4': raise KeyError('Not the V4') except KeyError: -- cgit v1.2.1 From 5f174090a545e6a441d37f7fbe66153544d0e162 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 15:51:48 -0400 Subject: Have lookup return None when it fails, moving the moving much of the try/except logic into single-line fallback expressions. --- setuptools/msvc9_support.py | 73 ++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index aac6ca12..6e86b14d 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -165,7 +165,10 @@ class RegistryInfo: return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') def lookup(self, base, key): - return distutils.msvc9compiler.Reg.get_value(base, key) + try: + return distutils.msvc9compiler.Reg.get_value(base, key) + except KeyError: + pass def _query_vcvarsall(version, arch): @@ -178,27 +181,22 @@ def _query_vcvarsall(version, arch): reg_value = reg.lookup # Find Microsoft Visual Studio directory - try: - # Try to get it from registry - VsInstallDir = reg_value(reg.vs, '%0.1f' % version) - except KeyError: - # If fail, use default path - name = 'Microsoft Visual Studio %0.1f' % version - VsInstallDir = os.path.join(pi.program_files_x86, name) + name = 'Microsoft Visual Studio %0.1f' % version + default_vs = os.path.join(pi.program_files_x86, name) + VsInstallDir = reg_value(reg.vs, '%0.1f' % version) or default_vs # Find Microsoft Visual C++ directory - try: - # Try to get it from registry - VcInstallDir = reg_value(reg.vc, '%0.1f' % version) - except KeyError: - try: - # Try to get "VC++ for Python" version from registry - install_base = reg_value(reg.vc_for_python, 'installdir') - VcInstallDir = os.path.join(install_base, 'VC') - except KeyError: - # If fail, use default path - default = r'Microsoft Visual Studio %0.1f\VC' % version - VcInstallDir = os.path.join(pi.program_files_x86, default) + + # If fail, use default path + default = r'Microsoft Visual Studio %0.1f\VC' % version + guess_vc = os.path.join(pi.program_files_x86, default) + + # Try to get "VC++ for Python" version from registry + install_base = reg_value(reg.vc_for_python, 'installdir') + default_vc = os.path.join(install_base, 'VC') if install_base else guess_vc + + VcInstallDir = reg_value(reg.vc, '%0.1f' % version) or default_vc + if not os.path.isdir(VcInstallDir): msg = 'vcvarsall.bat and Visual C++ directory not found' raise distutils.errors.DistutilsPlatformError(msg) @@ -213,19 +211,15 @@ def _query_vcvarsall(version, arch): WindowsSdkVer = () for ver in WindowsSdkVer: # Try to get it from registry - try: - loc = os.path.join(reg.windows_sdk, 'v%s' % ver) - WindowsSdkDir = reg_value(loc, 'installationfolder') + loc = os.path.join(reg.windows_sdk, 'v%s' % ver) + WindowsSdkDir = reg_value(loc, 'installationfolder') + if WindowsSdkDir: break - except KeyError: - pass if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # Try to get "VC++ for Python" version from registry - try: - install_base = reg_value(reg.vc_for_python, 'installdir') + install_base = reg_value(reg.vc_for_python, 'installdir') + if install_base: WindowsSdkDir = os.path.join(install_base, 'WinSDK') - except: - pass if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # If fail, use default path for ver in WindowsSdkVer: @@ -238,23 +232,20 @@ def _query_vcvarsall(version, arch): WindowsSdkDir = os.path.join(VcInstallDir, 'PlatformSDK') # Find Microsoft .NET Framework 32bit directory - try: - # Try to get it from registry - FrameworkDir32 = reg_value(reg.vc, 'frameworkdir32') - except KeyError: - # If fail, use default path - FrameworkDir32 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') + guess_fw = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') + FrameworkDir32 = reg_value(reg.vc, 'frameworkdir32') or guess_fw # Find Microsoft .NET Framework 64bit directory - try: - # Try to get it from registry - FrameworkDir64 = reg_value(reg.vc, 'frameworkdir64') - except KeyError: - # If fail, use default path - FrameworkDir64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') + guess_fw64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') + FrameworkDir64 = reg_value(reg.vc, 'frameworkdir64') or guess_fw64 # Find Microsoft .NET Framework Versions if version == 10.0: + v4 = reg_value(reg.vc, 'frameworkver32') + if v4 and v4.lower()[:2] != 'v4': + v4 = None + v4 = v4 or 'v4.0.30319' + try: # Try to get v4 from registry v4 = reg_value(reg.vc, 'frameworkver32') -- cgit v1.2.1 From 758b4249b82b63657fe1b31d59810b4ee098473c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 15:53:08 -0400 Subject: Remove another hanging indent --- setuptools/msvc9_support.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 6e86b14d..fbf81bab 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -241,19 +241,11 @@ def _query_vcvarsall(version, arch): # Find Microsoft .NET Framework Versions if version == 10.0: - v4 = reg_value(reg.vc, 'frameworkver32') - if v4 and v4.lower()[:2] != 'v4': + v4 = reg_value(reg.vc, 'frameworkver32') or '' + if v4.lower()[:2] != 'v4': v4 = None + # default to last v4 version v4 = v4 or 'v4.0.30319' - - try: - # Try to get v4 from registry - v4 = reg_value(reg.vc, 'frameworkver32') - if v4.lower()[:2] != 'v4': - raise KeyError('Not the V4') - except KeyError: - # If fail, use last v4 version - v4 = 'v4.0.30319' FrameworkVer = (v4, 'v3.5') elif version == 9.0: FrameworkVer = ('v3.5', 'v2.0.50727') @@ -261,8 +253,10 @@ def _query_vcvarsall(version, arch): FrameworkVer = ('v3.0', 'v2.0.50727') # Set Microsoft Visual Studio Tools - VSTools = [os.path.join(VsInstallDir, r'Common7\IDE'), - os.path.join(VsInstallDir, r'Common7\Tools')] + VSTools = [ + os.path.join(VsInstallDir, r'Common7\IDE'), + os.path.join(VsInstallDir, r'Common7\Tools'), + ] # Set Microsoft Visual C++ Includes VCIncludes = [os.path.join(VcInstallDir, 'Include')] -- cgit v1.2.1 From 6d11de6d754a73307dd62c7ba499c3f14c5f675b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 15:56:47 -0400 Subject: Move finding of visual studio into RegistryInfo --- setuptools/msvc9_support.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index fbf81bab..7728eb79 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -164,6 +164,14 @@ class RegistryInfo: def windows_sdk(self): return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') + def find_visual_studio(self): + """ + Find Microsoft Visual Studio directory + """ + name = 'Microsoft Visual Studio %0.1f' % self.version + default = os.path.join(self.platform_info.program_files_x86, name) + return self.lookup(self.vs, '%0.1f' % self.version) or default + def lookup(self, base, key): try: return distutils.msvc9compiler.Reg.get_value(base, key) @@ -180,11 +188,6 @@ def _query_vcvarsall(version, arch): reg = RegistryInfo(pi, version) reg_value = reg.lookup - # Find Microsoft Visual Studio directory - name = 'Microsoft Visual Studio %0.1f' % version - default_vs = os.path.join(pi.program_files_x86, name) - VsInstallDir = reg_value(reg.vs, '%0.1f' % version) or default_vs - # Find Microsoft Visual C++ directory # If fail, use default path @@ -254,8 +257,8 @@ def _query_vcvarsall(version, arch): # Set Microsoft Visual Studio Tools VSTools = [ - os.path.join(VsInstallDir, r'Common7\IDE'), - os.path.join(VsInstallDir, r'Common7\Tools'), + os.path.join(reg.find_visual_studio(), r'Common7\IDE'), + os.path.join(reg.find_visual_studio(), r'Common7\Tools'), ] # Set Microsoft Visual C++ Includes @@ -302,7 +305,7 @@ def _query_vcvarsall(version, arch): FxTools.append(os.path.join(FrameworkDir64, ver)) # Set Microsoft Visual Studio Team System Database - VsTDb = [os.path.join(VsInstallDir, r'VSTSDB\Deploy')] + VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] # Return Environment Variables env = {} -- cgit v1.2.1 From 0dc21cb9a6a5195f1b7e6ae47901b32fb9b486eb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:17:48 -0500 Subject: Extract find_visual_c method into RegistryInfo --- setuptools/msvc9_support.py | 50 ++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 7728eb79..4a1ec54e 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -172,6 +172,26 @@ class RegistryInfo: default = os.path.join(self.platform_info.program_files_x86, name) return self.lookup(self.vs, '%0.1f' % self.version) or default + def find_visual_c(self): + """ + Find Microsoft Visual C++ directory + """ + # If fail, use default path + default = r'Microsoft Visual Studio %0.1f\VC' % self.version + guess_vc = os.path.join(self.platform_info.program_files_x86, default) + + # Try to get "VC++ for Python" version from registry + install_base = self.lookup(self.vc_for_python, 'installdir') + default_vc = os.path.join(install_base, 'VC') if install_base else guess_vc + + result = self.lookup(self.vc, '%0.1f' % self.version) or default_vc + + if not os.path.isdir(result): + msg = 'vcvarsall.bat and Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) + + return result + def lookup(self, base, key): try: return distutils.msvc9compiler.Reg.get_value(base, key) @@ -188,22 +208,6 @@ def _query_vcvarsall(version, arch): reg = RegistryInfo(pi, version) reg_value = reg.lookup - # Find Microsoft Visual C++ directory - - # If fail, use default path - default = r'Microsoft Visual Studio %0.1f\VC' % version - guess_vc = os.path.join(pi.program_files_x86, default) - - # Try to get "VC++ for Python" version from registry - install_base = reg_value(reg.vc_for_python, 'installdir') - default_vc = os.path.join(install_base, 'VC') if install_base else guess_vc - - VcInstallDir = reg_value(reg.vc, '%0.1f' % version) or default_vc - - if not os.path.isdir(VcInstallDir): - msg = 'vcvarsall.bat and Visual C++ directory not found' - raise distutils.errors.DistutilsPlatformError(msg) - # Find Microsoft Windows SDK directory WindowsSdkDir = '' if version == 9.0: @@ -232,7 +236,7 @@ def _query_vcvarsall(version, arch): WindowsSdkDir = d if not WindowsSdkDir: # If fail, use Platform SDK - WindowsSdkDir = os.path.join(VcInstallDir, 'PlatformSDK') + WindowsSdkDir = os.path.join(reg.find_visual_c(), 'PlatformSDK') # Find Microsoft .NET Framework 32bit directory guess_fw = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') @@ -262,21 +266,21 @@ def _query_vcvarsall(version, arch): ] # Set Microsoft Visual C++ Includes - VCIncludes = [os.path.join(VcInstallDir, 'Include')] + VCIncludes = [os.path.join(reg.find_visual_c(), 'Include')] # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries VCLibraries = [ - os.path.join(VcInstallDir, 'Lib' + pi.lib_extra), - os.path.join(VcInstallDir, r'ATLMFC\LIB' + pi.lib_extra), + os.path.join(reg.find_visual_c(), 'Lib' + pi.lib_extra), + os.path.join(reg.find_visual_c(), r'ATLMFC\LIB' + pi.lib_extra), ] # Set Microsoft Visual C++ Tools VCTools = [ - os.path.join(VcInstallDir, 'VCPackages'), - os.path.join(VcInstallDir, 'Bin' + pi.tools_extra), + os.path.join(reg.find_visual_c(), 'VCPackages'), + os.path.join(reg.find_visual_c(), 'Bin' + pi.tools_extra), ] if pi.tools_extra: - VCTools.append(os.path.join(VcInstallDir, 'Bin')) + VCTools.append(os.path.join(reg.find_visual_c(), 'Bin')) # Set Microsoft Windows SDK Include OSLibraries = [os.path.join(WindowsSdkDir, 'Lib' + pi.sdk_extra)] -- cgit v1.2.1 From 0a3b6506627f7caddc400ce779d9a9dc4b3505e5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:22:12 -0500 Subject: Extract find_windows_sdk method in RegistryInfo. --- setuptools/msvc9_support.py | 78 ++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 4a1ec54e..8fb532e7 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -192,6 +192,40 @@ class RegistryInfo: return result + def find_windows_sdk(self): + """ + Find Microsoft Windows SDK directory + """ + WindowsSdkDir = '' + if self.version == 9.0: + WindowsSdkVer = ('7.0', '6.1', '6.0a') + elif self.version == 10.0: + WindowsSdkVer = ('7.1', '7.0a') + else: + WindowsSdkVer = () + for ver in WindowsSdkVer: + # Try to get it from registry + loc = os.path.join(self.windows_sdk, 'v%s' % ver) + WindowsSdkDir = self.lookup(loc, 'installationfolder') + if WindowsSdkDir: + break + if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): + # Try to get "VC++ for Python" version from registry + install_base = self.lookup(self.vc_for_python, 'installdir') + if install_base: + WindowsSdkDir = os.path.join(install_base, 'WinSDK') + if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): + # If fail, use default path + for ver in WindowsSdkVer: + path = r'Microsoft SDKs\Windows\v%s' % ver + d = os.path.join(self.platform_info.program_files, path) + if os.path.isdir(d): + WindowsSdkDir = d + if not WindowsSdkDir: + # If fail, use Platform SDK + WindowsSdkDir = os.path.join(self.find_visual_c(), 'PlatformSDK') + return WindowsSdkDir + def lookup(self, base, key): try: return distutils.msvc9compiler.Reg.get_value(base, key) @@ -208,36 +242,6 @@ def _query_vcvarsall(version, arch): reg = RegistryInfo(pi, version) reg_value = reg.lookup - # Find Microsoft Windows SDK directory - WindowsSdkDir = '' - if version == 9.0: - WindowsSdkVer = ('7.0', '6.1', '6.0a') - elif version == 10.0: - WindowsSdkVer = ('7.1', '7.0a') - else: - WindowsSdkVer = () - for ver in WindowsSdkVer: - # Try to get it from registry - loc = os.path.join(reg.windows_sdk, 'v%s' % ver) - WindowsSdkDir = reg_value(loc, 'installationfolder') - if WindowsSdkDir: - break - if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): - # Try to get "VC++ for Python" version from registry - install_base = reg_value(reg.vc_for_python, 'installdir') - if install_base: - WindowsSdkDir = os.path.join(install_base, 'WinSDK') - if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): - # If fail, use default path - for ver in WindowsSdkVer: - path = r'Microsoft SDKs\Windows\v%s' % ver - d = os.path.join(pi.program_files, path) - if os.path.isdir(d): - WindowsSdkDir = d - if not WindowsSdkDir: - # If fail, use Platform SDK - WindowsSdkDir = os.path.join(reg.find_visual_c(), 'PlatformSDK') - # Find Microsoft .NET Framework 32bit directory guess_fw = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') FrameworkDir32 = reg_value(reg.vc, 'frameworkdir32') or guess_fw @@ -283,24 +287,24 @@ def _query_vcvarsall(version, arch): VCTools.append(os.path.join(reg.find_visual_c(), 'Bin')) # Set Microsoft Windows SDK Include - OSLibraries = [os.path.join(WindowsSdkDir, 'Lib' + pi.sdk_extra)] + OSLibraries = [os.path.join(reg.find_windows_sdk(), 'Lib' + pi.sdk_extra)] # Set Microsoft Windows SDK Libraries OSIncludes = [ - os.path.join(WindowsSdkDir, 'Include'), - os.path.join(WindowsSdkDir, r'Include\gl'), + os.path.join(reg.find_windows_sdk(), 'Include'), + os.path.join(reg.find_windows_sdk(), r'Include\gl'), ] # Set Microsoft Windows SDK Tools - SdkTools = [os.path.join(WindowsSdkDir, 'Bin')] + SdkTools = [os.path.join(reg.find_windows_sdk(), 'Bin')] if not pi.target_is_x86(): - SdkTools.append(os.path.join(WindowsSdkDir, 'Bin' + pi.sdk_extra)) + SdkTools.append(os.path.join(reg.find_windows_sdk(), 'Bin' + pi.sdk_extra)) if version == 10.0: path = r'Bin\NETFX 4.0 Tools' + pi.sdk_extra - SdkTools.append(os.path.join(WindowsSdkDir, path)) + SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) # Set Microsoft Windows SDK Setup - SdkSetup = [os.path.join(WindowsSdkDir, 'Setup')] + SdkSetup = [os.path.join(reg.find_windows_sdk(), 'Setup')] # Set Microsoft .NET Framework Tools FxTools = [os.path.join(FrameworkDir32, ver) for ver in FrameworkVer] -- cgit v1.2.1 From 22c3b2f50df1f0cadc39eb359037ca83e2689dcc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:27:35 -0500 Subject: Extract find_dot_net_versions method in RegistryInfo. --- setuptools/msvc9_support.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 8fb532e7..ca71addb 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -226,6 +226,23 @@ class RegistryInfo: WindowsSdkDir = os.path.join(self.find_visual_c(), 'PlatformSDK') return WindowsSdkDir + def find_dot_net_versions(self): + """ + Find Microsoft .NET Framework Versions + """ + if self.version == 10.0: + v4 = self.lookup(self.vc, 'frameworkver32') or '' + if v4.lower()[:2] != 'v4': + v4 = None + # default to last v4 version + v4 = v4 or 'v4.0.30319' + FrameworkVer = (v4, 'v3.5') + elif self.version == 9.0: + FrameworkVer = ('v3.5', 'v2.0.50727') + elif self.version == 8.0: + FrameworkVer = ('v3.0', 'v2.0.50727') + return FrameworkVer + def lookup(self, base, key): try: return distutils.msvc9compiler.Reg.get_value(base, key) @@ -250,19 +267,6 @@ def _query_vcvarsall(version, arch): guess_fw64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') FrameworkDir64 = reg_value(reg.vc, 'frameworkdir64') or guess_fw64 - # Find Microsoft .NET Framework Versions - if version == 10.0: - v4 = reg_value(reg.vc, 'frameworkver32') or '' - if v4.lower()[:2] != 'v4': - v4 = None - # default to last v4 version - v4 = v4 or 'v4.0.30319' - FrameworkVer = (v4, 'v3.5') - elif version == 9.0: - FrameworkVer = ('v3.5', 'v2.0.50727') - elif version == 8.0: - FrameworkVer = ('v3.0', 'v2.0.50727') - # Set Microsoft Visual Studio Tools VSTools = [ os.path.join(reg.find_visual_studio(), r'Common7\IDE'), @@ -307,9 +311,9 @@ def _query_vcvarsall(version, arch): SdkSetup = [os.path.join(reg.find_windows_sdk(), 'Setup')] # Set Microsoft .NET Framework Tools - FxTools = [os.path.join(FrameworkDir32, ver) for ver in FrameworkVer] + FxTools = [os.path.join(FrameworkDir32, ver) for ver in reg.find_dot_net_versions()] if not pi.target_is_x86() and not pi.current_is_x86(): - for ver in FrameworkVer: + for ver in reg.find_dot_net_versions(): FxTools.append(os.path.join(FrameworkDir64, ver)) # Set Microsoft Visual Studio Team System Database -- cgit v1.2.1 From 904ecaa94be171a5d7750ea81e1327ab91d07057 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:34:51 -0500 Subject: Rewrite for/append loop as simple list comprehension on the product of roots and versions. --- setuptools/msvc9_support.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index ca71addb..b15ebeec 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -1,4 +1,5 @@ import os +import itertools import distutils.errors try: @@ -311,10 +312,14 @@ def _query_vcvarsall(version, arch): SdkSetup = [os.path.join(reg.find_windows_sdk(), 'Setup')] # Set Microsoft .NET Framework Tools - FxTools = [os.path.join(FrameworkDir32, ver) for ver in reg.find_dot_net_versions()] - if not pi.target_is_x86() and not pi.current_is_x86(): - for ver in reg.find_dot_net_versions(): - FxTools.append(os.path.join(FrameworkDir64, ver)) + roots = [FrameworkDir32] + include_64_framework = not pi.target_is_x86() and not pi.current_is_x86() + roots += [FrameworkDir64] if include_64_framework else [] + + FxTools = [ + os.path.join(root, ver) + for root, ver in itertools.product(roots, reg.find_dot_net_versions()) + ] # Set Microsoft Visual Studio Team System Database VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] -- cgit v1.2.1 From 124b0d651e42aa93e4ef1735245cdbd76f3e86fa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:36:17 -0500 Subject: Rewrite init/set with singular construction. --- setuptools/msvc9_support.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index b15ebeec..a82803e5 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -325,11 +325,12 @@ def _query_vcvarsall(version, arch): VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] # Return Environment Variables - env = {} - env['include'] = [VCIncludes, OSIncludes] - env['lib'] = [VCLibraries, OSLibraries, FxTools] - env['libpath'] = [VCLibraries, FxTools] - env['path'] = [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, FxTools] + env = dict( + include=[VCIncludes, OSIncludes], + lib=[VCLibraries, OSLibraries, FxTools], + libpath=[VCLibraries, FxTools], + path=[VCTools, VSTools, VsTDb, SdkTools, SdkSetup, FxTools], + ) def checkpath(path, varlist): # Function that add valid paths in list in not already present -- cgit v1.2.1 From 5688d67516e28349157fa239dae102470da82ca3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:40:08 -0500 Subject: Replace last usage of reg_value --- setuptools/msvc9_support.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index a82803e5..25d89875 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -258,15 +258,14 @@ def _query_vcvarsall(version, arch): """ pi = PlatformInfo(arch) reg = RegistryInfo(pi, version) - reg_value = reg.lookup # Find Microsoft .NET Framework 32bit directory guess_fw = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') - FrameworkDir32 = reg_value(reg.vc, 'frameworkdir32') or guess_fw + FrameworkDir32 = reg.lookup(reg.vc, 'frameworkdir32') or guess_fw # Find Microsoft .NET Framework 64bit directory guess_fw64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') - FrameworkDir64 = reg_value(reg.vc, 'frameworkdir64') or guess_fw64 + FrameworkDir64 = reg.lookup(reg.vc, 'frameworkdir64') or guess_fw64 # Set Microsoft Visual Studio Tools VSTools = [ -- cgit v1.2.1 From 37bea1dab1aff98021a4d7bab986cafac1d2a537 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 19:42:50 -0500 Subject: Correct find/replace error --- setuptools/msvc9_support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 25d89875..855cdc87 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -352,7 +352,7 @@ def _query_vcvarsall(version, arch): # Format paths to Environment Variable string if var: - env[key] = ';'.os.path.join(var) + env[key] = ';'.join(var) else: msg = "%s environment variable is empty" % key.upper() raise distutils.errors.DistutilsPlatformError(msg) -- cgit v1.2.1 From 57f3b022aa3054e7ea281091d782292d5895086c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 20:50:51 -0500 Subject: Rewrite env builder routine to a simple function, _build_paths, using iterables and common recipes. --- setuptools/msvc9_support.py | 77 +++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 855cdc87..85a8cf44 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -7,6 +7,9 @@ try: except ImportError: pass +import six + + unpatched = dict() def patch_for_specialized_compiler(): @@ -323,37 +326,49 @@ def _query_vcvarsall(version, arch): # Set Microsoft Visual Studio Team System Database VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] - # Return Environment Variables - env = dict( - include=[VCIncludes, OSIncludes], - lib=[VCLibraries, OSLibraries, FxTools], - libpath=[VCLibraries, FxTools], - path=[VCTools, VSTools, VsTDb, SdkTools, SdkSetup, FxTools], + return dict( + include=_build_paths('include', [VCIncludes, OSIncludes]), + lib=_build_paths('lib', [VCLibraries, OSLibraries, FxTools]), + libpath=_build_paths('libpath', [VCLibraries, FxTools]), + path=_build_paths('path', [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, FxTools]), ) - def checkpath(path, varlist): - # Function that add valid paths in list in not already present - if os.path.isdir(path) and path not in varlist: - varlist.append(path) - - for key in env.keys(): - var = [] - # Add valid paths - for val in env[key]: - for subval in val: - checkpath(subval, var) - # Add values from actual environment - try: - for val in os.environ[key].split(';'): - checkpath(val, var) - except KeyError: - pass - - # Format paths to Environment Variable string - if var: - env[key] = ';'.join(var) - else: - msg = "%s environment variable is empty" % key.upper() - raise distutils.errors.DistutilsPlatformError(msg) - return env +def _build_paths(name, spec_path_lists): + """ + Given an environment variable name and specified paths, + return a pathsep-separated string of paths containing + unique, extant, directories from those paths and from + the environment variable. Raise an error if no paths + are resolved. + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = os.environ.get(name, '').split(os.pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(os.path.isdir, paths)) + if not extant_paths: + msg = "%s environment variable is empty" % name.upper() + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = unique_everseen(extant_paths) + return os.pathsep.join(unique_paths) + + +# from Python docs +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + filterfalse = six.moves.filterfalse + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element -- cgit v1.2.1 From 80ee0f54d4408fac616ae12b7dd60b6c514f98d3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 21:10:07 -0500 Subject: Avoid setting a variable when the value is never used. --- setuptools/msvc9_support.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 85a8cf44..e1e08414 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -59,12 +59,12 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): # Try to get environement from vcvarsall.bat (Classical way) try: return unpatched['query_vcvarsall'](version, arch, *args, **kwargs) - except distutils.errors.DistutilsPlatformError as exc: + except distutils.errors.DistutilsPlatformError: # Error if Vcvarsall.bat is missing - message = exc.args[0] - except ValueError as exc: + pass + except ValueError: # Error if environment not set after executing vcvarsall.bat - message = exc.args[0] + pass # If vcvarsall.bat fail, try to set environment directly try: -- cgit v1.2.1 From b85f1ed5eaa2eca271b28a0aaddfebadfb8dedde Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 21:11:35 -0500 Subject: Rename function again for clarity. --- setuptools/msvc9_support.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index e1e08414..c0587f31 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -68,7 +68,7 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): # If vcvarsall.bat fail, try to set environment directly try: - return _query_vcvarsall(version, arch) + return _compute_env(version, arch) except distutils.errors.DistutilsPlatformError as exc: # Error if MSVC++ directory not found or environment not set message = exc.args[0] @@ -254,7 +254,7 @@ class RegistryInfo: pass -def _query_vcvarsall(version, arch): +def _compute_env(version, arch): """ Return environment variables for specified Microsoft Visual C++ version and platform. -- cgit v1.2.1 From dd70b3b6f6c00d753d7ff80ed8cf74448606e266 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 21:19:37 -0500 Subject: Extract function for _augment_exception. --- setuptools/msvc9_support.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index c0587f31..e80fb859 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -54,8 +54,6 @@ def find_vcvarsall(version): return unpatched['find_vcvarsall'](version) def query_vcvarsall(version, arch='x86', *args, **kwargs): - message = '' - # Try to get environement from vcvarsall.bat (Classical way) try: return unpatched['query_vcvarsall'](version, arch, *args, **kwargs) @@ -70,10 +68,18 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): try: return _compute_env(version, arch) except distutils.errors.DistutilsPlatformError as exc: - # Error if MSVC++ directory not found or environment not set - message = exc.args[0] + _augment_exception(exc, version) + raise + + +def _augment_exception(exc, version): + """ + Add details to the exception message to help guide the user + as to what action will resolve it. + """ + # Error if MSVC++ directory not found or environment not set + message = exc.args[0] - # Raise error if message and "vcvarsall.bat" in message: # Special error message if MSVC++ not installed message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ @@ -88,7 +94,7 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): message += ' Get it with "Microsoft Windows SDK for Windows 7": ' message += r'www.microsoft.com/download/details.aspx?id=8279' - raise distutils.errors.DistutilsPlatformError(message) + exc.args[0] = message class PlatformInfo: -- cgit v1.2.1 From a75fd253b2d77fb4896146c6dbf6d4aca0a756d9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2016 21:20:29 -0500 Subject: I don't imagine message will ever resolve to False --- setuptools/msvc9_support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index e80fb859..714035d3 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -80,7 +80,7 @@ def _augment_exception(exc, version): # Error if MSVC++ directory not found or environment not set message = exc.args[0] - if message and "vcvarsall.bat" in message: + if "vcvarsall.bat" in message: # Special error message if MSVC++ not installed message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ (version, message) -- cgit v1.2.1 From 75282e5568039e3dae52ee41ada35baf56f0567c Mon Sep 17 00:00:00 2001 From: JGoutin Date: Wed, 2 Mar 2016 18:51:40 +0000 Subject: First, thanks for the review. This is very instructive for me and the code is clearer now. I tested your reviewed version, and it work (Tested only on Windows x86, Python 3.4, Windows SDK 7.1 actually). I did the following changes : * Add many docstrings, and improve existing ones. * Change some comments that was unclear. * Move .NET Framework directory/version finders in "RegistryInfo", like others. * Started to add support for the MSVC++14.0 standalone compiler. (Note that this is Work in progress). This also help me to find what fail in actual structure. * Add "msvc9_" prefix to actual patch functions and unpatched dict for better readability. * Add VCforPython27 alternative in _augment_exception for IA64 (Arch not supported by VCforPython27) * Replace "lib_extra" and "sdk_extra" by more generic "ccpu_dir" and "tcpu_dir" functions (For better compatibility with MSVC++14) * Changes some parts (like VSTools) for reduce line length, avoid repetitions and improve PEP8. * Make "unique_everseen" and "lookup" private. But, it is not finished, I need at least to do: - Finish MSVC++14 support. - Remove patch for "find_vcvarsall" for MSVC++9 ? (Now "query_vcvarsall" patch also support VC++ for Python 2.7) - Make more tests with more compilers/platforms/Python combination. - Check compatibility with MSVC++11 and 12. - Rename the file in "msvc_support.py" ? (not MSVC9 only) - Rename "RegistryInfo" ? - Make compute_env a class ? for split subparts like you did it for "RegistryInfo" and "PlatformInfo" --- setuptools/msvc9_support.py | 421 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 334 insertions(+), 87 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 714035d3..a956a5b0 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -1,22 +1,45 @@ +""" +This module improve support for Microsoft Visual C++ compilers. (Windows Only) +""" import os import itertools import distutils.errors try: - import distutils.msvc9compiler + # Distutil file for MSVC++ 9.0 and upper + import distutils.msvc9compiler as msvc9compiler +except ImportError: + pass + +try: + # Distutil file for MSVC++ 14.0 and upper + import distutils._msvccompiler as msvc14compiler except ImportError: pass -import six +import six unpatched = dict() + def patch_for_specialized_compiler(): """ - Patch functions in distutils.msvc9compiler to use the standalone compiler - build for Python (Windows only). Fall back to original behavior when the - standalone compiler is not available. + Patch functions in distutils to use standalone Microsoft Visual C++ + compilers. + + Known supported compilers: + -------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) + + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ if 'distutils' not in globals(): # The module isn't available to be patched @@ -26,13 +49,44 @@ def patch_for_specialized_compiler(): # Already patched return - unpatched.update(vars(distutils.msvc9compiler)) + 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: + pass + + try: + # Patch distutils._msvccompiler._get_vc_env + unpatched['msv14_get_vc_env'] = msvc14compiler._get_vc_env + msvc14compiler._get_vc_env = msvc14_get_vc_env + except: + pass - distutils.msvc9compiler.find_vcvarsall = find_vcvarsall - distutils.msvc9compiler.query_vcvarsall = query_vcvarsall -def find_vcvarsall(version): - Reg = distutils.msvc9compiler.Reg +def msvc9_find_vcvarsall(version): + """ + Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone + compiler build for Python (VCForPython). Fall back to original behavior + when the standalone compiler is not available. + + Known supported compilers + ------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + + Parameters + ---------- + version: float + Required Microsoft Visual C++ version. + + Return + ------ + vcvarsall.bat path: str + """ + Reg = msvc9compiler.Reg VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' key = VC_BASE % ('', version) try: @@ -51,12 +105,38 @@ def find_vcvarsall(version): if os.path.isfile(vcvarsall): return vcvarsall - return unpatched['find_vcvarsall'](version) + return unpatched['msvc9_find_vcvarsall'](version) -def query_vcvarsall(version, arch='x86', *args, **kwargs): + +def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): + """ + Patched "distutils.msvc9compiler.query_vcvarsall" for support standalones + compilers. + + Known supported compilers + ------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) + + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + + Parameters + ---------- + ver: float + Required Microsoft Visual C++ version. + arch: str + Target architecture. + + Return + ------ + environment: dict + """ # Try to get environement from vcvarsall.bat (Classical way) try: - return unpatched['query_vcvarsall'](version, arch, *args, **kwargs) + return unpatched['msvc9_query_vcvarsall'](ver, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError: # Error if Vcvarsall.bat is missing pass @@ -66,13 +146,46 @@ def query_vcvarsall(version, arch='x86', *args, **kwargs): # If vcvarsall.bat fail, try to set environment directly try: - return _compute_env(version, arch) + return _compute_env(ver, arch) except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, version) + _augment_exception(exc, ver, arch) raise -def _augment_exception(exc, version): +def msvc14_get_vc_env(plat_spec): + """ + Patched "distutils._msvccompiler._get_vc_env" for support standalones + compilers. + + Known supported compilers + ------------------------- + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + + Parameters + ---------- + plat_spec: str + Target architecture. + + Return + ------ + environment: dict + """ + try: + return unpatched['msv14_get_vc_env'](plat_spec) + except distutils.errors.DistutilsPlatformError: + # Error if Vcvarsall.bat is missing + pass + + # If vcvarsall.bat fail, try to set environment directly + try: + return _compute_env(version, plat_spec) + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, version, plat_spec) + raise + + +def _augment_exception(exc, version, arch): """ Add details to the exception message to help guide the user as to what action will resolve it. @@ -85,26 +198,40 @@ def _augment_exception(exc, version): message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ (version, message) if int(version) == 9: - # For VC++ 9.0 Redirect user to Vc++ for Python 2.7 : - # This redirection link is maintained by Microsoft. - # Contact vspython@microsoft.com if it needs updating. - message += r' Get it from http://aka.ms/vcpython27' + if arch.lower().find('ia64') > -1: + # For VC++ 9.0, if IA64 support is needed, redirect user + # to Windows SDK 7.0 + message += ' Get it with "Microsoft Windows SDK 7.0": ' + message += r'www.microsoft.com/download/details.aspx?id=3138' + else: + # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : + # This redirection link is maintained by Microsoft. + # Contact vspython@microsoft.com if it needs updating. + message += r' Get it from http://aka.ms/vcpython27' elif int(version) == 10: # For VC++ 10.0 Redirect user to Windows SDK 7.1 - message += ' Get it with "Microsoft Windows SDK for Windows 7": ' + message += ' Get it with "Microsoft Windows SDK 7.1": ' message += r'www.microsoft.com/download/details.aspx?id=8279' exc.args[0] = message class PlatformInfo: + """ + Find architecture informations and system paths. + + Parameters + ---------- + arch: str + Target architecture. + """ current_cpu = os.environ['processor_architecture'].lower() win_dir = os.environ['WinDir'] program_files = os.environ['ProgramFiles'] program_files_x86 = os.environ.get('ProgramFiles(x86)', program_files) def __init__(self, arch): - self.arch = arch + self.arch = arch.lower() @property def target_cpu(self): @@ -116,85 +243,157 @@ class PlatformInfo: def current_is_x86(self): return self.current_cpu != 'x86' - @property - def lib_extra(self): + def ccpu_dir(self, hidex86=False, x64=False): + """ + Current platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + subfolder: str (starting with'\') + """ return ( - r'\amd64' if self.target_cpu == 'amd64' else - r'\ia64' if self.target_cpu == 'ia64' else - '' + '' if (self.current_cpu == 'x86' and hidex86) else + r'\x64' if (self.current_cpu == 'amd64' and x64) else + r'\%s' % self.current_cpu ) - @property - def sdk_extra(self): + def tcpu_dir(self, hidex86=False, x64=False): + """ + Target platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + subfolder: str (starting with'\') + """ return ( - r'\x64' if self.target_cpu == 'amd64' else - r'\ia64' if self.target_cpu == 'ia64' else - '' + '' if (self.target_cpu == 'x86' and hidex86) else + r'\x64' if (self.target_cpu == 'amd64' and x64) else + r'\%s' % self.target_cpu ) - @property - def tools_extra(self): - path = self.lib_extra + def tools_extra(self, forcex86=False): + """ + Platform specific subfolder for Visual C++ Tools. + + Parameters + ---------- + forcex86: bool + If cross compilation, return 'x86' as current architecture even + if current acritecture is not x86. + + Return + ------ + subfolder: str (starting with'\') + """ + path = self.tcpu_dir(True) if self.target_cpu != self.current_cpu: - path = path.replace('\\', '\\x86_') + current = 'x86' if forcex86 else self.current_cpu + path = path.replace('\\', '\\%s_' % current) return path class RegistryInfo: + """ + Find Microsoft Visual C++ compiler related paths using registry or + default paths. + + Parameters + ---------- + platform_info: platform_info + "platform_info" instance. + version: float + Required Microsoft Visual C++ version. + """ def __init__(self, platform_info, version): - self.platform_info = platform_info + self.pi = platform_info self.version = version @property def microsoft(self): + """ + Microsoft registry path. + """ return os.path.join( 'Software', - '' if self.platform_info.current_is_x86() else 'Wow6432Node', + '' if self.pi.current_is_x86() else 'Wow6432Node', 'Microsoft', ) @property def sxs(self): + """ + Visual Studio SxS registry path. + """ return os.path.join(self.microsoft, r'VisualStudio\SxS') @property def vc(self): + """ + Visual C++ registry path. + """ return os.path.join(self.sxs, 'VC7') @property def vs(self): + """ + Visual Studio registry path. + """ return os.path.join(self.sxs, 'VS7') @property def vc_for_python(self): + """ + Visual C++ for Python. + """ path = r'DevDiv\VCForPython\%0.1f' % self.version return os.path.join(self.microsoft, path) @property def windows_sdk(self): + """ + Windows/Platform SDK registry path. + """ return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') def find_visual_studio(self): """ - Find Microsoft Visual Studio directory + Find Microsoft Visual Studio directory. """ + # Default path name = 'Microsoft Visual Studio %0.1f' % self.version - default = os.path.join(self.platform_info.program_files_x86, name) - return self.lookup(self.vs, '%0.1f' % self.version) or default + default = os.path.join(self.pi.program_files_x86, name) + + # Try to get path from registry, if fail use default path + return self._lookup(self.vs, '%0.1f' % self.version) or default def find_visual_c(self): """ - Find Microsoft Visual C++ directory + Find Microsoft Visual C++ directory. """ - # If fail, use default path + # Default path default = r'Microsoft Visual Studio %0.1f\VC' % self.version - guess_vc = os.path.join(self.platform_info.program_files_x86, default) + guess_vc = os.path.join(self.pi.program_files_x86, default) - # Try to get "VC++ for Python" version from registry - install_base = self.lookup(self.vc_for_python, 'installdir') - default_vc = os.path.join(install_base, 'VC') if install_base else guess_vc + # Try to get "VC++ for Python" path from registry as default path + python_vc = self._lookup(self.vc_for_python, 'installdir') + default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc - result = self.lookup(self.vc, '%0.1f' % self.version) or default_vc + # Try to get path from registry, if fail use default path + result = self._lookup(self.vc, '%0.1f' % self.version) or default_vc if not os.path.isdir(result): msg = 'vcvarsall.bat and Visual C++ directory not found' @@ -204,7 +403,7 @@ class RegistryInfo: def find_windows_sdk(self): """ - Find Microsoft Windows SDK directory + Find Microsoft Windows SDK directory. """ WindowsSdkDir = '' if self.version == 9.0: @@ -216,19 +415,19 @@ class RegistryInfo: for ver in WindowsSdkVer: # Try to get it from registry loc = os.path.join(self.windows_sdk, 'v%s' % ver) - WindowsSdkDir = self.lookup(loc, 'installationfolder') + WindowsSdkDir = self._lookup(loc, 'installationfolder') if WindowsSdkDir: break if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # Try to get "VC++ for Python" version from registry - install_base = self.lookup(self.vc_for_python, 'installdir') + install_base = self._lookup(self.vc_for_python, 'installdir') if install_base: WindowsSdkDir = os.path.join(install_base, 'WinSDK') if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): # If fail, use default path for ver in WindowsSdkVer: path = r'Microsoft SDKs\Windows\v%s' % ver - d = os.path.join(self.platform_info.program_files, path) + d = os.path.join(self.pi.program_files, path) if os.path.isdir(d): WindowsSdkDir = d if not WindowsSdkDir: @@ -238,10 +437,10 @@ class RegistryInfo: def find_dot_net_versions(self): """ - Find Microsoft .NET Framework Versions + Find Microsoft .NET Framework Versions. """ if self.version == 10.0: - v4 = self.lookup(self.vc, 'frameworkver32') or '' + v4 = self._lookup(self.vc, 'frameworkver32') or '' if v4.lower()[:2] != 'v4': v4 = None # default to last v4 version @@ -253,9 +452,29 @@ class RegistryInfo: FrameworkVer = ('v3.0', 'v2.0.50727') return FrameworkVer - def lookup(self, base, key): + def find_dot_net_32(self): + """ + Find Microsoft .NET Framework 32bit directory. + """ + # Default path + guess_fw = os.path.join(self.pi.win_dir, r'Microsoft.NET\Framework') + + # Try to get path from registry, if fail use default path + return self._lookup(self.vc, 'frameworkdir32') or guess_fw + + def find_dot_net_64(self): + """ + Find Microsoft .NET Framework 64bit directory. + """ + # Default path + guess_fw = os.path.join(self.pi.win_dir, r'Microsoft.NET\Framework64') + + # Try to get path from registry, if fail use default path + return self._lookup(self.vc, 'frameworkdir64') or guess_fw + + def _lookup(self, base, key): try: - return distutils.msvc9compiler.Reg.get_value(base, key) + return msvc9compiler.Reg.get_value(base, key) except KeyError: pass @@ -264,45 +483,56 @@ def _compute_env(version, arch): """ Return environment variables for specified Microsoft Visual C++ version and platform. + + Microsoft Visual C++ known compatibles versions + ----------------------------------------------- + 9.0, 10.0, 14.0 """ pi = PlatformInfo(arch) reg = RegistryInfo(pi, version) - # Find Microsoft .NET Framework 32bit directory - guess_fw = os.path.join(pi.win_dir, r'Microsoft.NET\Framework') - FrameworkDir32 = reg.lookup(reg.vc, 'frameworkdir32') or guess_fw - - # Find Microsoft .NET Framework 64bit directory - guess_fw64 = os.path.join(pi.win_dir, r'Microsoft.NET\Framework64') - FrameworkDir64 = reg.lookup(reg.vc, 'frameworkdir64') or guess_fw64 - # Set Microsoft Visual Studio Tools - VSTools = [ - os.path.join(reg.find_visual_studio(), r'Common7\IDE'), - os.path.join(reg.find_visual_studio(), r'Common7\Tools'), - ] + paths = [r'Common7\IDE', r'Common7\Tools'] + if version >= 14.0: + paths.append(r'Common7\IDE\CommonExtensions\Microsoft\TestWindow') + paths.append(r'Team Tools\Performance Tools') + paths.append(r'Team Tools\Performance Tools' + pi.ccpu_dir(True, True)) + VSTools = [os.path.join(reg.find_visual_studio(), path) for path in paths] - # Set Microsoft Visual C++ Includes - VCIncludes = [os.path.join(reg.find_visual_c(), 'Include')] + # Set Microsoft Visual C++ & Microsoft Foundation Class Includes + VCIncludes = [os.path.join(reg.find_visual_c(), 'Include'), + os.path.join(reg.find_visual_c(), 'ATLMFC\Include')] # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries - VCLibraries = [ - os.path.join(reg.find_visual_c(), 'Lib' + pi.lib_extra), - os.path.join(reg.find_visual_c(), r'ATLMFC\LIB' + pi.lib_extra), - ] + paths = ['Lib' + pi.tcpu_dir(True), r'ATLMFC\Lib' + pi.tcpu_dir(True)] + if version >= 14.0: + paths.append(r'Lib\store' + pi.tcpu_dir(True)) + VCLibraries = [os.path.join(reg.find_visual_c(), path) for path in paths] + + # Set Microsoft Visual C++ store references Libraries + if version >= 14.0: + path = r'Lib\store\references' + VCStoreRefs = [os.path.join(reg.find_visual_c(), path)] + else: + VCStoreRefs = [] # Set Microsoft Visual C++ Tools + path = 'Bin' + pi.tools_extra(False if version >= 14.0 else True) VCTools = [ os.path.join(reg.find_visual_c(), 'VCPackages'), - os.path.join(reg.find_visual_c(), 'Bin' + pi.tools_extra), + os.path.join(reg.find_visual_c(), path), ] - if pi.tools_extra: + if pi.tools_extra() and version >= 14.0: + path = 'Bin' + pi.ccpu_dir(True) + VCTools.append(os.path.join(reg.find_visual_c(), path)) + else: VCTools.append(os.path.join(reg.find_visual_c(), 'Bin')) - # Set Microsoft Windows SDK Include - OSLibraries = [os.path.join(reg.find_windows_sdk(), 'Lib' + pi.sdk_extra)] - # Set Microsoft Windows SDK Libraries + path = 'Lib' + pi.tcpu_dir(True, True) + OSLibraries = [os.path.join(reg.find_windows_sdk(), path)] + + # Set Microsoft Windows SDK Include OSIncludes = [ os.path.join(reg.find_windows_sdk(), 'Include'), os.path.join(reg.find_windows_sdk(), r'Include\gl'), @@ -311,18 +541,19 @@ def _compute_env(version, arch): # Set Microsoft Windows SDK Tools SdkTools = [os.path.join(reg.find_windows_sdk(), 'Bin')] if not pi.target_is_x86(): - SdkTools.append(os.path.join(reg.find_windows_sdk(), 'Bin' + pi.sdk_extra)) + path = 'Bin' + pi.tcpu_dir(True, True) + SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) if version == 10.0: - path = r'Bin\NETFX 4.0 Tools' + pi.sdk_extra + path = r'Bin\NETFX 4.0 Tools' + pi.tcpu_dir(True, True) SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) # Set Microsoft Windows SDK Setup SdkSetup = [os.path.join(reg.find_windows_sdk(), 'Setup')] # Set Microsoft .NET Framework Tools - roots = [FrameworkDir32] + roots = [reg.find_dot_net_32()] include_64_framework = not pi.target_is_x86() and not pi.current_is_x86() - roots += [FrameworkDir64] if include_64_framework else [] + roots += [reg.find_dot_net_64()] if include_64_framework else [] FxTools = [ os.path.join(root, ver) @@ -332,11 +563,27 @@ def _compute_env(version, arch): # Set Microsoft Visual Studio Team System Database VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] + # Set Microsoft Build Engine + path = r'\MSBuild\%0.1f\bin%s' % (version, pi.ccpu_dir(True)) + MSBuild = [ + os.path.join(pi.program_files_x86, path), + os.path.join(pi.program_files, path) + ] + + # Set Microsoft HTML Help Workshop + path = 'HTML Help Workshop' + HTMLWork = [ + os.path.join(pi.program_files_x86, path), + os.path.join(pi.program_files, path) + ] + + # Return environment return dict( include=_build_paths('include', [VCIncludes, OSIncludes]), lib=_build_paths('lib', [VCLibraries, OSLibraries, FxTools]), - libpath=_build_paths('libpath', [VCLibraries, FxTools]), - path=_build_paths('path', [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, FxTools]), + libpath=_build_paths('libpath', [VCLibraries, FxTools, VCStoreRefs]), + path=_build_paths('path', [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, + FxTools, MSBuild, HTMLWork]), ) @@ -356,12 +603,12 @@ def _build_paths(name, spec_path_lists): if not extant_paths: msg = "%s environment variable is empty" % name.upper() raise distutils.errors.DistutilsPlatformError(msg) - unique_paths = unique_everseen(extant_paths) + unique_paths = _unique_everseen(extant_paths) return os.pathsep.join(unique_paths) # from Python docs -def unique_everseen(iterable, key=None): +def _unique_everseen(iterable, key=None): "List unique elements, preserving order. Remember all elements ever seen." # unique_everseen('AAAABBBCCDAABBB') --> A B C D # unique_everseen('ABBCcAD', str.lower) --> A B C D -- cgit v1.2.1 From 72db3cf5ff66ddab3f6e14880f5545d759938ae6 Mon Sep 17 00:00:00 2001 From: JGoutin Date: Thu, 3 Mar 2016 18:00:58 +0000 Subject: * Move non registry parts from RegistryInfo to SystemInfo. * Rename many variable to use the same names as in MSVC .bat files. This really help to compare the Python script with original sources. * Split compute_env to EnvironmentInfo class. * Continue to add support for MSVC14. * Some more little fixes. --- setuptools/msvc9_support.py | 775 +++++++++++++++++++++++++++++--------------- 1 file changed, 509 insertions(+), 266 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index a956a5b0..8835c22d 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -4,6 +4,7 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) import os import itertools import distutils.errors +import winreg try: # Distutil file for MSVC++ 9.0 and upper @@ -30,16 +31,16 @@ def patch_for_specialized_compiler(): Known supported compilers: -------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ if 'distutils' not in globals(): # The module isn't available to be patched @@ -74,8 +75,8 @@ def msvc9_find_vcvarsall(version): Known supported compilers ------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) Parameters ---------- @@ -115,13 +116,13 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): Known supported compilers ------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) Parameters ---------- @@ -138,15 +139,15 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): try: return unpatched['msvc9_query_vcvarsall'](ver, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError: - # Error if Vcvarsall.bat is missing + # Pass error if Vcvarsall.bat is missing pass except ValueError: - # Error if environment not set after executing vcvarsall.bat + # Pass error if environment not set after executing vcvarsall.bat pass - # If vcvarsall.bat fail, try to set environment directly + # If error, try to set environment directly try: - return _compute_env(ver, arch) + return EnvironmentInfo(arch, ver).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, ver, arch) raise @@ -159,8 +160,8 @@ def msvc14_get_vc_env(plat_spec): Known supported compilers ------------------------- - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) Parameters ---------- @@ -171,21 +172,22 @@ def msvc14_get_vc_env(plat_spec): ------ environment: dict """ + # Try to get environement from vcvarsall.bat (Classical way) try: return unpatched['msv14_get_vc_env'](plat_spec) except distutils.errors.DistutilsPlatformError: - # Error if Vcvarsall.bat is missing + # Pass error Vcvarsall.bat is missing pass - # If vcvarsall.bat fail, try to set environment directly + # If error, try to set environment directly try: - return _compute_env(version, plat_spec) + return EnvironmentInfo(plat_spec, vcvermin=14.0).return_env() except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, version, plat_spec) + _augment_exception(exc, 14.0) raise -def _augment_exception(exc, version, arch): +def _augment_exception(exc, version, arch=''): """ Add details to the exception message to help guide the user as to what action will resolve it. @@ -193,32 +195,33 @@ def _augment_exception(exc, version, arch): # Error if MSVC++ directory not found or environment not set message = exc.args[0] - if "vcvarsall.bat" in message: + if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ (version, message) - if int(version) == 9: + msdownload = r'www.microsoft.com/download/details.aspx?id=%d' + if version == 9.0: if arch.lower().find('ia64') > -1: # For VC++ 9.0, if IA64 support is needed, redirect user # to Windows SDK 7.0 message += ' Get it with "Microsoft Windows SDK 7.0": ' - message += r'www.microsoft.com/download/details.aspx?id=3138' + message += msdownload % 3138 else: # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : # This redirection link is maintained by Microsoft. # Contact vspython@microsoft.com if it needs updating. message += r' Get it from http://aka.ms/vcpython27' - elif int(version) == 10: + elif version == 10.0: # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK 7.1": ' - message += r'www.microsoft.com/download/details.aspx?id=8279' + message += msdownload % 8279 exc.args[0] = message class PlatformInfo: """ - Find architecture informations and system paths. + Current and Target Architectures informations. Parameters ---------- @@ -226,9 +229,6 @@ class PlatformInfo: Target architecture. """ current_cpu = os.environ['processor_architecture'].lower() - win_dir = os.environ['WinDir'] - program_files = os.environ['ProgramFiles'] - program_files_x86 = os.environ.get('ProgramFiles(x86)', program_files) def __init__(self, arch): self.arch = arch.lower() @@ -241,9 +241,9 @@ class PlatformInfo: return self.target_cpu == 'x86' def current_is_x86(self): - return self.current_cpu != 'x86' + return self.current_cpu == 'x86' - def ccpu_dir(self, hidex86=False, x64=False): + def current_dir(self, hidex86=False, x64=False): """ Current platform specific subfolder. @@ -256,7 +256,8 @@ class PlatformInfo: Return ------ - subfolder: str (starting with'\') + subfolder: str + "\target" """ return ( '' if (self.current_cpu == 'x86' and hidex86) else @@ -264,7 +265,7 @@ class PlatformInfo: r'\%s' % self.current_cpu ) - def tcpu_dir(self, hidex86=False, x64=False): + def target_dir(self, hidex86=False, x64=False): """ Target platform specific subfolder. @@ -277,7 +278,8 @@ class PlatformInfo: Return ------ - subfolder: str (starting with'\') + subfolder: str + "\current" """ return ( '' if (self.target_cpu == 'x86' and hidex86) else @@ -285,9 +287,9 @@ class PlatformInfo: r'\%s' % self.target_cpu ) - def tools_extra(self, forcex86=False): + def cross_dir(self, forcex86=False): """ - Platform specific subfolder for Visual C++ Tools. + Cross platform specific subfolder. Parameters ---------- @@ -297,9 +299,11 @@ class PlatformInfo: Return ------ - subfolder: str (starting with'\') + subfolder: str + "\current" if target architecture is current architecture, + "\current_target" if not. """ - path = self.tcpu_dir(True) + path = self.target_dir(True) if self.target_cpu != self.current_cpu: current = 'x86' if forcex86 else self.current_cpu path = path.replace('\\', '\\%s_' % current) @@ -308,24 +312,25 @@ class PlatformInfo: class RegistryInfo: """ - Find Microsoft Visual C++ compiler related paths using registry or - default paths. + Microsoft Visual Studio related registry informations. Parameters ---------- - platform_info: platform_info - "platform_info" instance. - version: float - Required Microsoft Visual C++ version. + platform_info: PlatformInfo + "PlatformInfo" instance. """ - def __init__(self, platform_info, version): + HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + + def __init__(self, platform_info): self.pi = platform_info - self.version = version @property def microsoft(self): """ - Microsoft registry path. + Microsoft software registry key. """ return os.path.join( 'Software', @@ -336,292 +341,530 @@ class RegistryInfo: @property def sxs(self): """ - Visual Studio SxS registry path. + Microsoft Visual Studio SxS registry key. """ return os.path.join(self.microsoft, r'VisualStudio\SxS') @property def vc(self): """ - Visual C++ registry path. + Microsoft Visual C++ registry key. """ return os.path.join(self.sxs, 'VC7') @property def vs(self): """ - Visual Studio registry path. + Microsoft Visual Studio registry key. """ return os.path.join(self.sxs, 'VS7') @property def vc_for_python(self): """ - Visual C++ for Python. + Microsoft Visual C++ for Python registry key. """ - path = r'DevDiv\VCForPython\%0.1f' % self.version + path = r'DevDiv\VCForPython' return os.path.join(self.microsoft, path) @property def windows_sdk(self): """ - Windows/Platform SDK registry path. + Microsoft Windows/Platform SDK registry key. """ return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') - def find_visual_studio(self): + def lookup(self, key, name): + """ + Look for values in registry. + + Parameters + ---------- + key: str + Registry key path where look. + name: str + Value name to find. + + Return + ------ + str: value + """ + for hkey in self.HKEYS: + try: + bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except FileNotFoundError: + continue + try: + return winreg.QueryValueEx(bkey, name)[0] + except FileNotFoundError: + pass + + +class SystemInfo: + """ + Microsoft Windows and Visual Studio related system inormations. + + Parameters + ---------- + registry_info: RegistryInfo + "RegistryInfo" instance. + vcver: float + Required Microsoft Visual C++ version. + """ + WinDir = os.environ['WinDir'] + ProgramFiles = os.environ['ProgramFiles'] + ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) + + def __init__(self, registry_info, vcver=None): + self.ri = registry_info + if vcver: + self.vcver = vcver + else: + try: + self.vcver = self.find_availables_vcver()[-1] + except IndexError: + err = 'No Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + def find_availables_vcver(self): + """ + Find all availables Microsoft Visual C++ versions. + """ + vckeys = (self.ri.vc, self.ri.vc_for_python) + vsvers = [] + for hkey in self.ri.HKEYS: + for key in vckeys: + try: + bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except FileNotFoundError: + continue + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + try: + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vsvers: + vsvers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vsvers: + vsvers.append(ver) + except ValueError: + pass + return sorted(vsvers) + + @property + def VSInstallDir(self): """ - Find Microsoft Visual Studio directory. + Microsoft Visual Studio directory. """ # Default path - name = 'Microsoft Visual Studio %0.1f' % self.version - default = os.path.join(self.pi.program_files_x86, name) + name = 'Microsoft Visual Studio %0.1f' % self.vcver + default = os.path.join(self.ProgramFilesx86, name) # Try to get path from registry, if fail use default path - return self._lookup(self.vs, '%0.1f' % self.version) or default + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vcver) or default - def find_visual_c(self): + @property + def VCInstallDir(self): """ - Find Microsoft Visual C++ directory. + Microsoft Visual C++ directory. """ # Default path - default = r'Microsoft Visual Studio %0.1f\VC' % self.version - guess_vc = os.path.join(self.pi.program_files_x86, default) + default = r'Microsoft Visual Studio %0.1f\VC' % self.vcver + guess_vc = os.path.join(self.ProgramFilesx86, default) # Try to get "VC++ for Python" path from registry as default path - python_vc = self._lookup(self.vc_for_python, 'installdir') + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vcver) + python_vc = self.ri.lookup(path, 'installdir') default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc # Try to get path from registry, if fail use default path - result = self._lookup(self.vc, '%0.1f' % self.version) or default_vc + result = self.ri.lookup(self.ri.vc, '%0.1f' % self.vcver) or default_vc if not os.path.isdir(result): - msg = 'vcvarsall.bat and Visual C++ directory not found' + msg = 'Microsoft Visual C++ directory not found' raise distutils.errors.DistutilsPlatformError(msg) return result - def find_windows_sdk(self): + @property + def WindowsSdkDir(self): """ - Find Microsoft Windows SDK directory. + Microsoft Windows SDK directory. """ - WindowsSdkDir = '' - if self.version == 9.0: - WindowsSdkVer = ('7.0', '6.1', '6.0a') - elif self.version == 10.0: - WindowsSdkVer = ('7.1', '7.0a') + sdkdir = '' + if self.vcver == 9.0: + sdkver = ('7.0', '6.1', '6.0a') + elif self.vcver == 10.0: + sdkver = ('7.1', '7.0a') + elif self.vcver == 14.0: + sdkver = ('10.0', '8.1', '8.1a') else: - WindowsSdkVer = () - for ver in WindowsSdkVer: + sdkver = () + for ver in sdkver: # Try to get it from registry - loc = os.path.join(self.windows_sdk, 'v%s' % ver) - WindowsSdkDir = self._lookup(loc, 'installationfolder') - if WindowsSdkDir: + loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: break - if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): + if not sdkdir or not os.path.isdir(sdkdir): # Try to get "VC++ for Python" version from registry - install_base = self._lookup(self.vc_for_python, 'installdir') + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vcver) + install_base = self.ri.lookup(path, 'installdir') if install_base: - WindowsSdkDir = os.path.join(install_base, 'WinSDK') - if not WindowsSdkDir or not os.path.isdir(WindowsSdkDir): - # If fail, use default path - for ver in WindowsSdkVer: + sdkdir = os.path.join(install_base, 'WinSDK') + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default new path + for ver in sdkver: + intver = ver[:ver.rfind('.')] + path = r'Microsoft SDKs\Windows Kits\%s' % (intver) + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default old path + for ver in sdkver: path = r'Microsoft SDKs\Windows\v%s' % ver - d = os.path.join(self.pi.program_files, path) + d = os.path.join(self.ProgramFiles, path) if os.path.isdir(d): - WindowsSdkDir = d - if not WindowsSdkDir: + sdkdir = d + if not sdkdir: # If fail, use Platform SDK - WindowsSdkDir = os.path.join(self.find_visual_c(), 'PlatformSDK') - return WindowsSdkDir - - def find_dot_net_versions(self): - """ - Find Microsoft .NET Framework Versions. - """ - if self.version == 10.0: - v4 = self._lookup(self.vc, 'frameworkver32') or '' - if v4.lower()[:2] != 'v4': - v4 = None - # default to last v4 version - v4 = v4 or 'v4.0.30319' - FrameworkVer = (v4, 'v3.5') - elif self.version == 9.0: - FrameworkVer = ('v3.5', 'v2.0.50727') - elif self.version == 8.0: - FrameworkVer = ('v3.0', 'v2.0.50727') - return FrameworkVer + sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') + return sdkdir - def find_dot_net_32(self): + @property + def FrameworkDir32(self): """ - Find Microsoft .NET Framework 32bit directory. + Microsoft .NET Framework 32bit directory. """ # Default path - guess_fw = os.path.join(self.pi.win_dir, r'Microsoft.NET\Framework') + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') # Try to get path from registry, if fail use default path - return self._lookup(self.vc, 'frameworkdir32') or guess_fw + return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw - def find_dot_net_64(self): + @property + def FrameworkDir64(self): """ - Find Microsoft .NET Framework 64bit directory. + Microsoft .NET Framework 64bit directory. """ # Default path - guess_fw = os.path.join(self.pi.win_dir, r'Microsoft.NET\Framework64') + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') # Try to get path from registry, if fail use default path - return self._lookup(self.vc, 'frameworkdir64') or guess_fw + return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw - def _lookup(self, base, key): - try: - return msvc9compiler.Reg.get_value(base, key) - except KeyError: - pass + @property + def FrameworkVersion32(self): + """ + Microsoft .NET Framework 32bit versions. + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self): + """ + Microsoft .NET Framework 64bit versions. + """ + return self._find_dot_net_versions(64) + def _find_dot_net_versions(self, bits=32): + """ + Find Microsoft .NET Framework versions. -def _compute_env(version, arch): + Parameters + ---------- + bits: int + Platform number of bits: 32 or 64. + """ + # Find actual .NET version + ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' + + # Set .NET versions for specified MSVC++ version + if self.vcver >= 14.0: + frameworkver = (ver, 'v4.0') + elif self.vcver == 10.0: + if ver.lower()[:2] != 'v4': + ver = '' + ver = ver or 'v4.0.30319' + frameworkver = (ver, 'v3.5') + elif self.vcver == 9.0: + frameworkver = ('v3.5', 'v2.0.50727') + elif self.vcver == 8.0: + frameworkver = ('v3.0', 'v2.0.50727') + return frameworkver + + +class EnvironmentInfo: """ Return environment variables for specified Microsoft Visual C++ version - and platform. + and platform : Lib, Include, Path and libpath. - Microsoft Visual C++ known compatibles versions - ----------------------------------------------- - 9.0, 10.0, 14.0 - """ - pi = PlatformInfo(arch) - reg = RegistryInfo(pi, version) - - # Set Microsoft Visual Studio Tools - paths = [r'Common7\IDE', r'Common7\Tools'] - if version >= 14.0: - paths.append(r'Common7\IDE\CommonExtensions\Microsoft\TestWindow') - paths.append(r'Team Tools\Performance Tools') - paths.append(r'Team Tools\Performance Tools' + pi.ccpu_dir(True, True)) - VSTools = [os.path.join(reg.find_visual_studio(), path) for path in paths] - - # Set Microsoft Visual C++ & Microsoft Foundation Class Includes - VCIncludes = [os.path.join(reg.find_visual_c(), 'Include'), - os.path.join(reg.find_visual_c(), 'ATLMFC\Include')] - - # Set Microsoft Visual C++ & Microsoft Foundation Class Libraries - paths = ['Lib' + pi.tcpu_dir(True), r'ATLMFC\Lib' + pi.tcpu_dir(True)] - if version >= 14.0: - paths.append(r'Lib\store' + pi.tcpu_dir(True)) - VCLibraries = [os.path.join(reg.find_visual_c(), path) for path in paths] - - # Set Microsoft Visual C++ store references Libraries - if version >= 14.0: - path = r'Lib\store\references' - VCStoreRefs = [os.path.join(reg.find_visual_c(), path)] - else: - VCStoreRefs = [] - - # Set Microsoft Visual C++ Tools - path = 'Bin' + pi.tools_extra(False if version >= 14.0 else True) - VCTools = [ - os.path.join(reg.find_visual_c(), 'VCPackages'), - os.path.join(reg.find_visual_c(), path), - ] - if pi.tools_extra() and version >= 14.0: - path = 'Bin' + pi.ccpu_dir(True) - VCTools.append(os.path.join(reg.find_visual_c(), path)) - else: - VCTools.append(os.path.join(reg.find_visual_c(), 'Bin')) - - # Set Microsoft Windows SDK Libraries - path = 'Lib' + pi.tcpu_dir(True, True) - OSLibraries = [os.path.join(reg.find_windows_sdk(), path)] - - # Set Microsoft Windows SDK Include - OSIncludes = [ - os.path.join(reg.find_windows_sdk(), 'Include'), - os.path.join(reg.find_windows_sdk(), r'Include\gl'), - ] - - # Set Microsoft Windows SDK Tools - SdkTools = [os.path.join(reg.find_windows_sdk(), 'Bin')] - if not pi.target_is_x86(): - path = 'Bin' + pi.tcpu_dir(True, True) - SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) - if version == 10.0: - path = r'Bin\NETFX 4.0 Tools' + pi.tcpu_dir(True, True) - SdkTools.append(os.path.join(reg.find_windows_sdk(), path)) - - # Set Microsoft Windows SDK Setup - SdkSetup = [os.path.join(reg.find_windows_sdk(), 'Setup')] - - # Set Microsoft .NET Framework Tools - roots = [reg.find_dot_net_32()] - include_64_framework = not pi.target_is_x86() and not pi.current_is_x86() - roots += [reg.find_dot_net_64()] if include_64_framework else [] - - FxTools = [ - os.path.join(root, ver) - for root, ver in itertools.product(roots, reg.find_dot_net_versions()) - ] - - # Set Microsoft Visual Studio Team System Database - VsTDb = [os.path.join(reg.find_visual_studio(), r'VSTSDB\Deploy')] - - # Set Microsoft Build Engine - path = r'\MSBuild\%0.1f\bin%s' % (version, pi.ccpu_dir(True)) - MSBuild = [ - os.path.join(pi.program_files_x86, path), - os.path.join(pi.program_files, path) - ] - - # Set Microsoft HTML Help Workshop - path = 'HTML Help Workshop' - HTMLWork = [ - os.path.join(pi.program_files_x86, path), - os.path.join(pi.program_files, path) - ] - - # Return environment - return dict( - include=_build_paths('include', [VCIncludes, OSIncludes]), - lib=_build_paths('lib', [VCLibraries, OSLibraries, FxTools]), - libpath=_build_paths('libpath', [VCLibraries, FxTools, VCStoreRefs]), - path=_build_paths('path', [VCTools, VSTools, VsTDb, SdkTools, SdkSetup, - FxTools, MSBuild, HTMLWork]), - ) - - -def _build_paths(name, spec_path_lists): - """ - Given an environment variable name and specified paths, - return a pathsep-separated string of paths containing - unique, extant, directories from those paths and from - the environment variable. Raise an error if no paths - are resolved. + This function is compatible with Microsoft Visual C++ 9.0 to 14.0. + + Parameters + ---------- + arch: str + Target architecture. + vcver: float + Required Microsoft Visual C++ version. If not set, autodetect the last + version. + vcvermin: float + Minimum Microsoft Visual C++ version. """ - # flatten spec_path_lists - spec_paths = itertools.chain.from_iterable(spec_path_lists) - env_paths = os.environ.get(name, '').split(os.pathsep) - paths = itertools.chain(spec_paths, env_paths) - extant_paths = list(filter(os.path.isdir, paths)) - if not extant_paths: - msg = "%s environment variable is empty" % name.upper() - raise distutils.errors.DistutilsPlatformError(msg) - unique_paths = _unique_everseen(extant_paths) - return os.pathsep.join(unique_paths) - - -# from Python docs -def _unique_everseen(iterable, key=None): - "List unique elements, preserving order. Remember all elements ever seen." - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D - seen = set() - seen_add = seen.add - filterfalse = six.moves.filterfalse - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) + def __init__(self, arch, vcver=None, vcvermin=None): + self.pi = PlatformInfo(arch) + self.ri = RegistryInfo(self.pi) + self.si = SystemInfo(self.ri, vcver) + + if self.vcver < vcvermin: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + @property + def vcver(self): + """ + Microsoft Visual C++ version. + """ + return self.si.vcver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + if self.vcver >= 14.0: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] + paths += [r'Team Tools\Performance Tools'] + paths += [r'Team Tools\Performance Tools%s' % arch_subdir] + return [os.path.join(self.si.VSInstallDir, path) for path in paths] + + @property + def VCIncludes(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Includes + """ + return [os.path.join(self.si.VCInstallDir, 'Include'), + os.path.join(self.si.VCInstallDir, 'ATLMFC\Include')] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries + """ + arch_subdir = self.pi.target_dir(hidex86=True) + paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] + if self.vcver >= 14.0: + paths += [r'Lib\store%s' % arch_subdir] + return [os.path.join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries + """ + path = os.path.join(self.si.VCInstallDir, r'Lib\store\references') + return [path] if self.vcver >= 14.0 else [] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools + """ + forcex86 = True if self.vcver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + tools = [ + os.path.join(self.si.VCInstallDir, 'VCPackages'), + os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir), + ] + if self.pi.cross_dir() and self.vcver >= 14.0: + path = 'Bin%s' % self.pi.current_dir(hidex86=True) + tools += [os.path.join(self.si.VCInstallDir, path)] + else: + tools += [os.path.join(self.si.VCInstallDir, 'Bin')] + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries + """ + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [os.path.join(self.si.WindowsSdkDir, 'Bin%s' % arch_subdir)] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include + """ + return [ + os.path.join(self.si.WindowsSdkDir, 'Include'), + os.path.join(self.si.WindowsSdkDir, r'Include\gl'), + ] + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools + """ + if self.vcver <= 10: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + else: + arch_subdir = self.pi.target_dir(x64=True) + tools = [os.path.join(self.si.WindowsSdkDir, 'Bin')] + if not self.pi.target_is_x86(): + path = 'Bin%s' % arch_subdir + tools += [os.path.join(self.si.WindowsSdkDir, path)] + if self.vcver == 10.0: + path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir + tools += [os.path.join(self.si.WindowsSdkDir, path)] + return tools + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup + """ + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools + """ + pi = self.pi + si = self.si + if self.vcver <= 10.0: + include32 = True + include64 = not pi.target_is_x86() and not pi.current_is_x86() + else: + include32 = pi.target_is_x86() or pi.current_is_x86() + include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' + tools = [] + if include32: + tools += [ + os.path.join(si.FrameworkDir32, ver) + for ver in si.FrameworkVersion32 + ] + if include64: + tools += [ + os.path.join(si.FrameworkDir64, ver) + for ver in si.FrameworkVersion64 + ] + return tools + + @property + def VsTDb(self): + """ + Microsoft Visual Studio Team System Database + """ + return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine + """ + arch_subdir = self.pi.current_dir(hidex86=True) + path = r'\MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) + return [ + os.path.join(self.si.ProgramFilesx86, path), + os.path.join(self.si.ProgramFiles, path) + ] + + @property + def HTMLWs(self): + """ + Microsoft HTML Help Workshop + """ + return [ + os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), + os.path.join(self.si.ProgramFiles, 'HTML Help Workshop') + ] + + @property + def VCRuntimeRedist(self): + """ + Microsoft Visual C++ runtime redistribuable dll + """ + arch_subdir = self.pi.target_dir(x64=True) + vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + vcruntime = vcruntime % (arch_subdir, self.vcver, self.vcver) + return os.path.join(self.si.VCInstallDir, vcruntime) + + def return_env(self): + """ + Return environment dict. + """ + env = dict( + include=self._build_paths('include', + [self.VCIncludes, + self.OSIncludes]), + lib=self._build_paths('lib', + [self.VCLibraries, + self.OSLibraries, + self.FxTools]), + libpath=self._build_paths('libpath', + [self.VCLibraries, + self.FxTools, + self.VCStoreRefs]), + path=self._build_paths('path', + [self.VCTools, + self.VSTools, + self.VsTDb, + self.SdkTools, + self.SdkSetup, + self.FxTools, + self.MSBuild, + self.HTMLWs]), + ) + if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): + env['py_vcruntime_redist'] = self.VCRuntimeRedist + return env + + def _build_paths(self, name, spec_path_lists): + """ + Given an environment variable name and specified paths, + return a pathsep-separated string of paths containing + unique, extant, directories from those paths and from + the environment variable. Raise an error if no paths + are resolved. + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = os.environ.get(name, '').split(os.pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(os.path.isdir, paths)) + if not extant_paths: + msg = "%s environment variable is empty" % name.upper() + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = self._unique_everseen(extant_paths) + return os.pathsep.join(unique_paths) + + # from Python docs + def _unique_everseen(self, iterable, key=None): + """ + List unique elements, preserving order. + Remember all elements ever seen. + """ + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + filterfalse = six.moves.filterfalse + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element -- cgit v1.2.1 From 321b27c9cc8e5a5e316488f457a890765a06f9ca Mon Sep 17 00:00:00 2001 From: JGoutin Date: Fri, 4 Mar 2016 15:54:44 +0000 Subject: * Continue do add support for MSVC14 (Still work in progress). * Add support for MSVC11 and 12 (Only few changes with actual code needed for them). * Python 2 compatibility for winreg. --- setuptools/msvc9_support.py | 150 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 31 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 8835c22d..0e144a80 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -4,23 +4,22 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) import os import itertools import distutils.errors -import winreg +import six +import six.moves.winreg as winreg try: - # Distutil file for MSVC++ 9.0 and upper + # 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 + # Distutil file for MSVC++ 14.0 and upper (Python 3.5) import distutils._msvccompiler as msvc14compiler except ImportError: pass -import six - unpatched = dict() @@ -73,6 +72,8 @@ def msvc9_find_vcvarsall(version): compiler build for Python (VCForPython). Fall back to original behavior when the standalone compiler is not available. + Redirect the path of "vcvarsall.bat". + Known supported compilers ------------------------- Microsoft Visual C++ 9.0: @@ -114,6 +115,8 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): Patched "distutils.msvc9compiler.query_vcvarsall" for support standalones compilers. + Set environment without use of "vcvarsall.bat". + Known supported compilers ------------------------- Microsoft Visual C++ 9.0: @@ -158,6 +161,8 @@ def msvc14_get_vc_env(plat_spec): Patched "distutils._msvccompiler._get_vc_env" for support standalones compilers. + Set environment without use of "vcvarsall.bat". + Known supported compilers ------------------------- Microsoft Visual C++ 14.0: @@ -338,24 +343,31 @@ class RegistryInfo: 'Microsoft', ) + @property + def visualstudio(self): + """ + Microsoft Visual Studio root registry key. + """ + return os.path.join(self.microsoft, r'VisualStudio') + @property def sxs(self): """ Microsoft Visual Studio SxS registry key. """ - return os.path.join(self.microsoft, r'VisualStudio\SxS') + return os.path.join(self.visualstudio, 'SxS') @property def vc(self): """ - Microsoft Visual C++ registry key. + Microsoft Visual C++ VC7 registry key. """ return os.path.join(self.sxs, 'VC7') @property def vs(self): """ - Microsoft Visual Studio registry key. + Microsoft Visual Studio VS7 registry key. """ return os.path.join(self.sxs, 'VS7') @@ -496,14 +508,16 @@ class SystemInfo: Microsoft Windows SDK directory. """ sdkdir = '' - if self.vcver == 9.0: + if self.vcver <= 9.0: sdkver = ('7.0', '6.1', '6.0a') elif self.vcver == 10.0: sdkver = ('7.1', '7.0a') - elif self.vcver == 14.0: - sdkver = ('10.0', '8.1', '8.1a') - else: - sdkver = () + elif self.vcver == 11.0: + sdkver = ('8.0', '8.0a') + elif self.vcver == 12.0: + sdkver = ('8.1', '8.1a') + elif self.vcver >= 14.0: + sdkver = ('10.0', '8.1') for ver in sdkver: # Try to get it from registry loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) @@ -536,6 +550,15 @@ class SystemInfo: sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') return sdkdir + @property + def FSharpInstallDir(self): + """ + Microsoft Visual F# directory. + """ + path = r'%0.1f\Setup\F#' % self.vcver + path = os.path.join(self.ri.visualstudio, path) + return self.ri.lookup(path, 'productdir') or '' + @property def FrameworkDir32(self): """ @@ -585,16 +608,16 @@ class SystemInfo: ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' # Set .NET versions for specified MSVC++ version - if self.vcver >= 14.0: + if self.vcver >= 12.0: frameworkver = (ver, 'v4.0') - elif self.vcver == 10.0: + elif self.vcver >= 10.0: if ver.lower()[:2] != 'v4': ver = '' ver = ver or 'v4.0.30319' frameworkver = (ver, 'v3.5') elif self.vcver == 9.0: frameworkver = ('v3.5', 'v2.0.50727') - elif self.vcver == 8.0: + if self.vcver == 8.0: frameworkver = ('v3.0', 'v2.0.50727') return frameworkver @@ -606,6 +629,9 @@ class EnvironmentInfo: This function is compatible with Microsoft Visual C++ 9.0 to 14.0. + Script created by analysing Microsoft environment configuration files like + "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... + Parameters ---------- arch: str @@ -669,8 +695,9 @@ class EnvironmentInfo: """ Microsoft Visual C++ store references Libraries """ - path = os.path.join(self.si.VCInstallDir, r'Lib\store\references') - return [path] if self.vcver >= 14.0 else [] + if self.vcver < 14.0: + return [] + return os.path.join(self.si.VCInstallDir, r'Lib\store\references') @property def VCTools(self): @@ -703,25 +730,36 @@ class EnvironmentInfo: """ Microsoft Windows SDK Include """ - return [ - os.path.join(self.si.WindowsSdkDir, 'Include'), - os.path.join(self.si.WindowsSdkDir, r'Include\gl'), - ] + if self.vcver <= 10.0: + return [ + os.path.join(self.si.WindowsSdkDir, 'Include'), + os.path.join(self.si.WindowsSdkDir, r'Include\gl') + ] + elif self.vcver <= 12.0: + return [ + os.path.join(self.si.WindowsSdkDir, r'include\shared'), + os.path.join(self.si.WindowsSdkDir, r'include\um'), + os.path.join(self.si.WindowsSdkDir, r'include\winrt') + ] @property def SdkTools(self): """ Microsoft Windows SDK Tools """ - if self.vcver <= 10: - arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + if self.vcver <= 11.0: + tools = [os.path.join(self.si.WindowsSdkDir, 'Bin')] else: - arch_subdir = self.pi.target_dir(x64=True) - tools = [os.path.join(self.si.WindowsSdkDir, 'Bin')] - if not self.pi.target_is_x86(): + tools = [os.path.join(self.si.WindowsSdkDir, r'Bin\x86')] + if not self.pi.current_is_x86(): + arch_subdir = self.pi.current_dir(x64=True) path = 'Bin%s' % arch_subdir tools += [os.path.join(self.si.WindowsSdkDir, path)] - if self.vcver == 10.0: + if self.vcver == 10.0 or self.vcver == 11.0: + if self.pi.target_is_x86(): + arch_subdir = '' + else: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir tools += [os.path.join(self.si.WindowsSdkDir, path)] return tools @@ -759,6 +797,22 @@ class EnvironmentInfo: ] return tools + @property + def NetFxSDKLibraries(self): + """ + Microsoft .Net Framework SDK Libraries + """ + if self.vcver < 14.0: + return [] + + @property + def NetFxSDKIncludes(self): + """ + Microsoft .Net Framework SDK Includes + """ + if self.vcver < 14.0: + return [] + @property def VsTDb(self): """ @@ -771,6 +825,8 @@ class EnvironmentInfo: """ Microsoft Build Engine """ + if self.vcver < 12.0: + return [] arch_subdir = self.pi.current_dir(hidex86=True) path = r'\MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) return [ @@ -783,11 +839,38 @@ class EnvironmentInfo: """ Microsoft HTML Help Workshop """ + if self.vcver < 11.0: + return [] return [ os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), os.path.join(self.si.ProgramFiles, 'HTML Help Workshop') ] + @property + def UCRTLibraries(self): + """ + Microsoft Universal CRT Libraries + """ + if self.vcver < 14.0: + return [] + + @property + def UCRTIncludes(self): + """ + Microsoft Universal CRT Include + """ + if self.vcver < 14.0: + return [] + + @property + def FSharp(self): + """ + Microsoft Visual F# + """ + if self.vcver < 11.0 and self.vcver > 12.0: + return [] + return self.si.FSharpInstallDir + @property def VCRuntimeRedist(self): """ @@ -805,11 +888,15 @@ class EnvironmentInfo: env = dict( include=self._build_paths('include', [self.VCIncludes, - self.OSIncludes]), + self.OSIncludes, + self.UCRTIncludes, + self.NetFxSDKIncludes]), lib=self._build_paths('lib', [self.VCLibraries, self.OSLibraries, - self.FxTools]), + self.FxTools, + self.UCRTLibraries, + self.NetFxSDKLibraries]), libpath=self._build_paths('libpath', [self.VCLibraries, self.FxTools, @@ -822,7 +909,8 @@ class EnvironmentInfo: self.SdkSetup, self.FxTools, self.MSBuild, - self.HTMLWs]), + self.HTMLWs, + self.FSharp]), ) if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): env['py_vcruntime_redist'] = self.VCRuntimeRedist -- cgit v1.2.1 From 2fbcd3426032bd175fbab63a7e26fba76369eeb9 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 17 Apr 2016 18:07:20 +0200 Subject: Feature/msvc discovery It's finished !! Now, it fully support MSVC from 9.0 to 14.0, and all theses standalone distributions: - Microsoft Visual C++ Compiler for Python 2.7 (MSVC++9.0, x86, amd64) - Microsoft Windows SDK 6.1 (MSVC++9.0, x86, x64, ia64) - Microsoft Windows SDK 7.0 (MSVC++9.0, x86, x64, ia64) - Microsoft Windows SDK 7.1 (MSVC++10.0, x86, x64, ia64) - Microsoft Visual C++ Build Tools 2015 (MSVC++14.0, x86, x64, arm) Next step: - Code review. - Test it with some MSVC versions/Architectures/Python versions combinations. I have also some comments on the global implementation: - I think `msvc9_find_vcvarsall` can now be deleted. - Maybe rename the file, by example "msvc_support.py". - Maybe Implementing it (And other distutils patchs) for not be only called by "setuptools", but also by "pkg_resources": This will automatically activate patchs for some externals packages like "Cython" transparently. --- setuptools/msvc9_support.py | 301 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 239 insertions(+), 62 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 0e144a80..860ab9f5 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -379,12 +379,33 @@ class RegistryInfo: path = r'DevDiv\VCForPython' return os.path.join(self.microsoft, path) + @property + def microsoft_sdk(self): + """ + Microsoft SDK registry key. + """ + return os.path.join(self.microsoft, 'Microsoft SDKs') + @property def windows_sdk(self): """ Microsoft Windows/Platform SDK registry key. """ - return os.path.join(self.microsoft, r'Microsoft SDKs\Windows') + return os.path.join(self.microsoft_sdk, 'Windows') + + @property + def netfx_sdk(self): + """ + Microsoft .NET Framework SDK registry key. + """ + return os.path.join(self.microsoft_sdk, 'NETFXSDK') + + @property + def windows_kits_roots(self): + """ + Microsoft Windows Kits Roots registry key. + """ + return os.path.join(self.microsoft, r'Windows Kits\Installed Roots') def lookup(self, key, name): """ @@ -429,6 +450,7 @@ class SystemInfo: def __init__(self, registry_info, vcver=None): self.ri = registry_info + self.pi = self.ri.pi if vcver: self.vcver = vcver else: @@ -503,22 +525,29 @@ class SystemInfo: return result @property - def WindowsSdkDir(self): + def WindowsSdkVersion(self): """ - Microsoft Windows SDK directory. + Microsoft Windows SDK versions. """ - sdkdir = '' + # Set Windows SDK versions for specified MSVC++ version if self.vcver <= 9.0: - sdkver = ('7.0', '6.1', '6.0a') + return ('7.0', '6.1', '6.0a') elif self.vcver == 10.0: - sdkver = ('7.1', '7.0a') + return ('7.1', '7.0a') elif self.vcver == 11.0: - sdkver = ('8.0', '8.0a') + return ('8.0', '8.0a') elif self.vcver == 12.0: - sdkver = ('8.1', '8.1a') + return ('8.1', '8.1a') elif self.vcver >= 14.0: - sdkver = ('10.0', '8.1') - for ver in sdkver: + return ('10.0', '8.1') + + @property + def WindowsSdkDir(self): + """ + Microsoft Windows SDK directory. + """ + sdkdir = '' + for ver in self.WindowsSdkVersion: # Try to get it from registry loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) sdkdir = self.ri.lookup(loc, 'installationfolder') @@ -532,7 +561,7 @@ class SystemInfo: sdkdir = os.path.join(install_base, 'WinSDK') if not sdkdir or not os.path.isdir(sdkdir): # If fail, use default new path - for ver in sdkver: + for ver in self.WindowsSdkVersion: intver = ver[:ver.rfind('.')] path = r'Microsoft SDKs\Windows Kits\%s' % (intver) d = os.path.join(self.ProgramFiles, path) @@ -540,7 +569,7 @@ class SystemInfo: sdkdir = d if not sdkdir or not os.path.isdir(sdkdir): # If fail, use default old path - for ver in sdkver: + for ver in self.WindowsSdkVersion: path = r'Microsoft SDKs\Windows\v%s' % ver d = os.path.join(self.ProgramFiles, path) if os.path.isdir(d): @@ -550,6 +579,37 @@ class SystemInfo: sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') return sdkdir + @property + def WindowsSDKExecutablePath(self): + """ + Microsoft Windows SDK executable directory. + """ + # Find WinSDK NetFx Tools registry dir name + if self.vcver <= 11.0: + netfxver = 35 + arch = '' + else: + netfxver = 40 + hidex86 = True if self.vcver <= 12.0 else False + arch = self.pi.current_dir(x64=True, hidex86=hidex86) + fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) + + # liste all possibles registry paths + regpaths = [] + if self.vcver >= 14.0: + for ver in self.NetFxSdkVersion: + regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] + + for ver in self.WindowsSdkVersion: + regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] + + # Return installation folder from the more recent path + for path in regpaths: + execpath = self.ri.lookup(path, 'installationfolder') + if execpath: + break + return execpath + @property def FSharpInstallDir(self): """ @@ -559,6 +619,48 @@ class SystemInfo: path = os.path.join(self.ri.visualstudio, path) return self.ri.lookup(path, 'productdir') or '' + @property + def UniversalCRTSdkDir(self): + """ + Microsoft Universal CRT SDK directory. + """ + # Set Kit Roots versions for specified MSVC++ version + if self.vcver >= 14.0: + vers = ('10', '81') + else: + vers = () + + # Find path of the more recent Kit + for ver in vers: + sdkdir = self.ri.lookup(self.ri.windows_kits_roots, + 'kitsroot%s' % ver) + if sdkdir: + break + return sdkdir or '' + + @property + def NetFxSdkVersion(self): + """ + Microsoft .NET Framework SDK versions. + """ + # Set FxSdk versions for specified MSVC++ version + if self.vcver >= 14.0: + return ('4.6.1', '4.6') + else: + return () + + @property + def NetFxSdkDir(self): + """ + Microsoft .NET Framework SDK directory. + """ + for ver in self.NetFxSdkVersion: + loc = os.path.join(self.ri.netfx_sdk, ver) + sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') + if sdkdir: + break + return sdkdir or '' + @property def FrameworkDir32(self): """ @@ -611,10 +713,8 @@ class SystemInfo: if self.vcver >= 12.0: frameworkver = (ver, 'v4.0') elif self.vcver >= 10.0: - if ver.lower()[:2] != 'v4': - ver = '' - ver = ver or 'v4.0.30319' - frameworkver = (ver, 'v3.5') + frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver, + 'v3.5') elif self.vcver == 9.0: frameworkver = ('v3.5', 'v2.0.50727') if self.vcver == 8.0: @@ -647,9 +747,10 @@ class EnvironmentInfo: self.ri = RegistryInfo(self.pi) self.si = SystemInfo(self.ri, vcver) - if self.vcver < vcvermin: - err = 'No suitable Microsoft Visual C++ version found' - raise distutils.errors.DistutilsPlatformError(err) + if vcvermin: + if self.vcver < vcvermin: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) @property def vcver(self): @@ -664,11 +765,13 @@ class EnvironmentInfo: Microsoft Visual Studio Tools """ paths = [r'Common7\IDE', r'Common7\Tools'] + if self.vcver >= 14.0: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] paths += [r'Team Tools\Performance Tools'] paths += [r'Team Tools\Performance Tools%s' % arch_subdir] + return [os.path.join(self.si.VSInstallDir, path) for path in paths] @property @@ -686,8 +789,10 @@ class EnvironmentInfo: """ arch_subdir = self.pi.target_dir(hidex86=True) paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] + if self.vcver >= 14.0: paths += [r'Lib\store%s' % arch_subdir] + return [os.path.join(self.si.VCInstallDir, path) for path in paths] @property @@ -706,15 +811,16 @@ class EnvironmentInfo: """ forcex86 = True if self.vcver <= 10.0 else False arch_subdir = self.pi.cross_dir(forcex86) - tools = [ - os.path.join(self.si.VCInstallDir, 'VCPackages'), - os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir), - ] + tools = [os.path.join(self.si.VCInstallDir, 'VCPackages'), + os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir)] + if self.pi.cross_dir() and self.vcver >= 14.0: path = 'Bin%s' % self.pi.current_dir(hidex86=True) tools += [os.path.join(self.si.VCInstallDir, path)] + else: tools += [os.path.join(self.si.VCInstallDir, 'Bin')] + return tools @property @@ -722,39 +828,71 @@ class EnvironmentInfo: """ Microsoft Windows SDK Libraries """ - arch_subdir = self.pi.target_dir(hidex86=True, x64=True) - return [os.path.join(self.si.WindowsSdkDir, 'Bin%s' % arch_subdir)] + if self.vcver <= 10.0: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [os.path.join(self.si.WindowsSdkDir, 'Bin%s' % arch_subdir)] + + else: + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.WindowsSdkDir, 'lib') + libver = self._get_content_dirname(lib) + return [os.path.join(lib, r'%sum%s' % (libver, arch_subdir))] @property def OSIncludes(self): """ Microsoft Windows SDK Include """ + include = os.path.join(self.si.WindowsSdkDir, 'include') + if self.vcver <= 10.0: - return [ - os.path.join(self.si.WindowsSdkDir, 'Include'), - os.path.join(self.si.WindowsSdkDir, r'Include\gl') - ] - elif self.vcver <= 12.0: - return [ - os.path.join(self.si.WindowsSdkDir, r'include\shared'), - os.path.join(self.si.WindowsSdkDir, r'include\um'), - os.path.join(self.si.WindowsSdkDir, r'include\winrt') - ] + return [include, os.path.join(include, 'gl')] + + else: + if self.vcver >= 14.0: + sdkver = self._get_content_dirname(include) + else: + sdkver = '' + return [os.path.join(include, '%sshared' % sdkver), + os.path.join(include, '%sum' % sdkver), + os.path.join(include, '%swinrt' % sdkver)] + + @property + def OSLibpath(self): + """ + Microsoft Windows SDK Libraries Paths + """ + ref = os.path.join(self.si.WindowsSdkDir, 'References') + libpath = [os.path.join(ref, r'CommonConfiguration\Neutral')] + + if self.vcver >= 14.0: + libpath += [ref, + os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), + os.path.join(ref, r'Windows.Foundation.' + r'UniversalApiContract\1.0.0.0'), + os.path.join(ref, r'Windows.Foundation.' + r'FoundationContract\1.0.0.0'), + os.path.join(ref, r'Windows.Networking.Connectivity.' + r'WwanContract\1.0.0.0'), + os.path.join(self.si.WindowsSdkDir, r'ExtensionSDKs' + r'\Microsoft.VCLibs\%0.1f\References' + r'\CommonConfiguration\neutral' % + self.vcver)] + return libpath @property def SdkTools(self): """ Microsoft Windows SDK Tools """ - if self.vcver <= 11.0: - tools = [os.path.join(self.si.WindowsSdkDir, 'Bin')] - else: - tools = [os.path.join(self.si.WindowsSdkDir, r'Bin\x86')] + tools = [os.path.join(self.si.WindowsSdkDir, + 'Bin' if self.vcver <= 11.0 else r'Bin\x86')] + if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) path = 'Bin%s' % arch_subdir tools += [os.path.join(self.si.WindowsSdkDir, path)] + if self.vcver == 10.0 or self.vcver == 11.0: if self.pi.target_is_x86(): arch_subdir = '' @@ -762,6 +900,10 @@ class EnvironmentInfo: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir tools += [os.path.join(self.si.WindowsSdkDir, path)] + + if self.si.WindowsSDKExecutablePath: + tools += [self.si.WindowsSDKExecutablePath] + return tools @property @@ -778,23 +920,21 @@ class EnvironmentInfo: """ pi = self.pi si = self.si + if self.vcver <= 10.0: include32 = True include64 = not pi.target_is_x86() and not pi.current_is_x86() else: include32 = pi.target_is_x86() or pi.current_is_x86() include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' + tools = [] if include32: - tools += [ - os.path.join(si.FrameworkDir32, ver) - for ver in si.FrameworkVersion32 - ] + tools += [os.path.join(si.FrameworkDir32, ver) + for ver in si.FrameworkVersion32] if include64: - tools += [ - os.path.join(si.FrameworkDir64, ver) - for ver in si.FrameworkVersion64 - ] + tools += [os.path.join(si.FrameworkDir64, ver) + for ver in si.FrameworkVersion64] return tools @property @@ -802,17 +942,22 @@ class EnvironmentInfo: """ Microsoft .Net Framework SDK Libraries """ - if self.vcver < 14.0: + if self.vcver < 14.0 or not self.si.NetFxSdkDir: return [] + arch_subdir = self.pi.target_dir(x64=True) + return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] + @property def NetFxSDKIncludes(self): """ Microsoft .Net Framework SDK Includes """ - if self.vcver < 14.0: + if self.vcver < 14.0 or not self.si.NetFxSdkDir: return [] + return [os.path.join(self.si.NetFxSdkDir, r'include\um')] + @property def VsTDb(self): """ @@ -827,24 +972,22 @@ class EnvironmentInfo: """ if self.vcver < 12.0: return [] + arch_subdir = self.pi.current_dir(hidex86=True) path = r'\MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) - return [ - os.path.join(self.si.ProgramFilesx86, path), - os.path.join(self.si.ProgramFiles, path) - ] + return [os.path.join(self.si.ProgramFilesx86, path), + os.path.join(self.si.ProgramFiles, path)] @property - def HTMLWs(self): + def HTMLHelpWorkshop(self): """ Microsoft HTML Help Workshop """ if self.vcver < 11.0: return [] - return [ - os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), - os.path.join(self.si.ProgramFiles, 'HTML Help Workshop') - ] + + return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), + os.path.join(self.si.ProgramFiles, 'HTML Help Workshop')] @property def UCRTLibraries(self): @@ -854,6 +997,11 @@ class EnvironmentInfo: if self.vcver < 14.0: return [] + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') + ucrtver = self._get_content_dirname(lib) + return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] + @property def UCRTIncludes(self): """ @@ -862,6 +1010,10 @@ class EnvironmentInfo: if self.vcver < 14.0: return [] + include = os.path.join(self.si.UniversalCRTSdkDir, 'include') + ucrtver = self._get_content_dirname(include) + return [os.path.join(include, '%sucrt' % ucrtver)] + @property def FSharp(self): """ @@ -869,6 +1021,7 @@ class EnvironmentInfo: """ if self.vcver < 11.0 and self.vcver > 12.0: return [] + return self.si.FSharpInstallDir @property @@ -900,7 +1053,8 @@ class EnvironmentInfo: libpath=self._build_paths('libpath', [self.VCLibraries, self.FxTools, - self.VCStoreRefs]), + self.VCStoreRefs, + self.OSLibpath]), path=self._build_paths('path', [self.VCTools, self.VSTools, @@ -909,7 +1063,7 @@ class EnvironmentInfo: self.SdkSetup, self.FxTools, self.MSBuild, - self.HTMLWs, + self.HTMLHelpWorkshop, self.FSharp]), ) if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): @@ -940,9 +1094,11 @@ class EnvironmentInfo: """ List unique elements, preserving order. Remember all elements ever seen. + + _unique_everseen('AAAABBBCCDAABBB') --> A B C D + + _unique_everseen('ABBCcAD', str.lower) --> A B C D """ - # unique_everseen('AAAABBBCCDAABBB') --> A B C D - # unique_everseen('ABBCcAD', str.lower) --> A B C D seen = set() seen_add = seen.add filterfalse = six.moves.filterfalse @@ -956,3 +1112,24 @@ class EnvironmentInfo: if k not in seen: seen_add(k) yield element + + def _get_content_dirname(self, path): + """ + Return name of the first dir in path or '' if no dir found. + + Parameters + ---------- + path: str + Path where search dir. + + Return + ------ + foldername: str + "name\" or "" + """ + try: + name = os.listdir(path) + if name: + return '%s\\' % name[0] + except FileNotFoundError: + return '' -- cgit v1.2.1 From d8587a79301f6eba5334f9e365188b9a9af1d2ac Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 17 Apr 2016 18:46:24 +0200 Subject: Update msvc9_support.py --- setuptools/msvc9_support.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 860ab9f5..38acc43f 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -1131,5 +1131,6 @@ class EnvironmentInfo: name = os.listdir(path) if name: return '%s\\' % name[0] + return '' except FileNotFoundError: return '' -- cgit v1.2.1 From f9e6b8fd2d46a2a4efabffaf51151652ef06e55b Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Fri, 22 Apr 2016 07:52:42 +0200 Subject: Fix six import --- setuptools/msvc9_support.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 38acc43f..58e34204 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -4,8 +4,7 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) import os import itertools import distutils.errors -import six -import six.moves.winreg as winreg +from setuptools.extern.six.moves import winreg, filterfalse try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) @@ -1101,7 +1100,6 @@ class EnvironmentInfo: """ seen = set() seen_add = seen.add - filterfalse = six.moves.filterfalse if key is None: for element in filterfalse(seen.__contains__, iterable): seen_add(element) -- cgit v1.2.1 From 11cd038b9bf7231b2007c8accb406062965fa8a0 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Fri, 29 Apr 2016 18:11:51 +0200 Subject: Tests and fixes Tests advancement: I tested WinSDK 7.1, WinSDK 7.0, WinSDK 6.1, VC for Python 2.7 with current arch as X86 and all possible target architecture. Environment generated from script is good for all (With some little fixes in this commit), some path are add on some case, I will look if I need to remove them. I need also to check .Net Framework 64bit in this case. I need also to do test for sames compilers on X64 architecture, I will also test MSVC++ 2015 Build tools on X64. I looked for create automated test on AppVeyor for this, but it is impossible to only have one of theses compilers only installed... So I did all test manually. --- setuptools/msvc9_support.py | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 58e34204..94de6fd1 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -235,7 +235,7 @@ class PlatformInfo: current_cpu = os.environ['processor_architecture'].lower() def __init__(self, arch): - self.arch = arch.lower() + self.arch = arch.lower().replace('x64', 'amd64') @property def target_cpu(self): @@ -810,7 +810,7 @@ class EnvironmentInfo: """ forcex86 = True if self.vcver <= 10.0 else False arch_subdir = self.pi.cross_dir(forcex86) - tools = [os.path.join(self.si.VCInstallDir, 'VCPackages'), + tools = [os.path.join(self.si.VCInstallDir, r'VCPackages'), os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir)] if self.pi.cross_dir() and self.vcver >= 14.0: @@ -829,7 +829,7 @@ class EnvironmentInfo: """ if self.vcver <= 10.0: arch_subdir = self.pi.target_dir(hidex86=True, x64=True) - return [os.path.join(self.si.WindowsSdkDir, 'Bin%s' % arch_subdir)] + return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] else: arch_subdir = self.pi.target_dir(x64=True) @@ -862,7 +862,13 @@ class EnvironmentInfo: Microsoft Windows SDK Libraries Paths """ ref = os.path.join(self.si.WindowsSdkDir, 'References') - libpath = [os.path.join(ref, r'CommonConfiguration\Neutral')] + libpath = [] + + if self.vcver <= 9.0: + libpath += self.OSLibraries + + if self.vcver >= 11.0: + libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] if self.vcver >= 14.0: libpath += [ref, @@ -910,6 +916,9 @@ class EnvironmentInfo: """ Microsoft Windows SDK Setup """ + if self.vcver > 9.0: + return [] + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] @property @@ -1033,27 +1042,35 @@ class EnvironmentInfo: vcruntime = vcruntime % (arch_subdir, self.vcver, self.vcver) return os.path.join(self.si.VCInstallDir, vcruntime) - def return_env(self): + def return_env(self, exists=True): """ Return environment dict. + + Parameters + ---------- + exists: bool + It True, only return existing paths. """ env = dict( include=self._build_paths('include', [self.VCIncludes, self.OSIncludes, self.UCRTIncludes, - self.NetFxSDKIncludes]), + self.NetFxSDKIncludes], + exists), lib=self._build_paths('lib', [self.VCLibraries, self.OSLibraries, self.FxTools, self.UCRTLibraries, - self.NetFxSDKLibraries]), + self.NetFxSDKLibraries], + exists), libpath=self._build_paths('libpath', [self.VCLibraries, self.FxTools, self.VCStoreRefs, - self.OSLibpath]), + self.OSLibpath], + exists), path=self._build_paths('path', [self.VCTools, self.VSTools, @@ -1063,13 +1080,14 @@ class EnvironmentInfo: self.FxTools, self.MSBuild, self.HTMLHelpWorkshop, - self.FSharp]), + self.FSharp], + exists), ) if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): env['py_vcruntime_redist'] = self.VCRuntimeRedist return env - def _build_paths(self, name, spec_path_lists): + def _build_paths(self, name, spec_path_lists, exists): """ Given an environment variable name and specified paths, return a pathsep-separated string of paths containing @@ -1081,7 +1099,7 @@ class EnvironmentInfo: spec_paths = itertools.chain.from_iterable(spec_path_lists) env_paths = os.environ.get(name, '').split(os.pathsep) paths = itertools.chain(spec_paths, env_paths) - extant_paths = list(filter(os.path.isdir, paths)) + extant_paths = list(filter(os.path.isdir, paths)) if exists else paths if not extant_paths: msg = "%s environment variable is empty" % name.upper() raise distutils.errors.DistutilsPlatformError(msg) -- cgit v1.2.1 From d4486dc692f07db138a129d08803ceb8f55ae4b8 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Fri, 29 Apr 2016 18:25:50 +0200 Subject: useless 'r' before str --- setuptools/msvc9_support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 94de6fd1..dba7c392 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -810,7 +810,7 @@ class EnvironmentInfo: """ forcex86 = True if self.vcver <= 10.0 else False arch_subdir = self.pi.cross_dir(forcex86) - tools = [os.path.join(self.si.VCInstallDir, r'VCPackages'), + tools = [os.path.join(self.si.VCInstallDir, 'VCPackages'), os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir)] if self.pi.cross_dir() and self.vcver >= 14.0: -- cgit v1.2.1 From 28ef4014b03a6d1b2428def3bb78e665679a5b12 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 2 May 2016 10:06:01 -0400 Subject: Restore ability for msvc9_support to be imported on non-Windows platforms. --- setuptools/msvc9_support.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index dba7c392..1737cc21 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -2,9 +2,26 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) """ import os +import collections import itertools import distutils.errors -from setuptools.extern.six.moves import winreg, filterfalse +from setuptools.extern.six.moves import filterfalse + +try: + from setuptools.extern.six.moves import winreg + safe_env = os.environ +except ImportError: + """ + Mock winreg and environ so the module can be imported + on this platform. + """ + class winreg: + HKEY_USERS = None + HKEY_CURRENT_USER = None + HKEY_LOCAL_MACHINE = None + HKEY_CLASSES_ROOT = None + safe_env = collections.defaultdict(lambda: '') + try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) @@ -232,7 +249,7 @@ class PlatformInfo: arch: str Target architecture. """ - current_cpu = os.environ['processor_architecture'].lower() + current_cpu = safe_env['processor_architecture'].lower() def __init__(self, arch): self.arch = arch.lower().replace('x64', 'amd64') @@ -443,8 +460,8 @@ class SystemInfo: vcver: float Required Microsoft Visual C++ version. """ - WinDir = os.environ['WinDir'] - ProgramFiles = os.environ['ProgramFiles'] + WinDir = safe_env['WinDir'] + ProgramFiles = safe_env['ProgramFiles'] ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) def __init__(self, registry_info, vcver=None): -- cgit v1.2.1 From 144f72286d67f8e0368333bf1c3115ebce18c374 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Mon, 2 May 2016 20:21:49 +0200 Subject: Some fixes After testing VC Builds Tools 2015 with current = Amd64 and target = Amd64, x86, Arm --- setuptools/msvc9_support.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 1737cc21..7cafd062 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -324,11 +324,11 @@ class PlatformInfo: "\current" if target architecture is current architecture, "\current_target" if not. """ - path = self.target_dir(True) - if self.target_cpu != self.current_cpu: - current = 'x86' if forcex86 else self.current_cpu - path = path.replace('\\', '\\%s_' % current) - return path + current = 'x86' if forcex86 else self.current_cpu + if self.target_cpu != current: + return self.target_dir().replace('\\', '\\%s_' % current) + else: + return '' class RegistryInfo: @@ -818,24 +818,27 @@ class EnvironmentInfo: """ if self.vcver < 14.0: return [] - return os.path.join(self.si.VCInstallDir, r'Lib\store\references') + return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] @property def VCTools(self): """ Microsoft Visual C++ Tools """ + si = self.si + tools = [os.path.join(si.VCInstallDir, 'VCPackages')] + forcex86 = True if self.vcver <= 10.0 else False arch_subdir = self.pi.cross_dir(forcex86) - tools = [os.path.join(self.si.VCInstallDir, 'VCPackages'), - os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir)] + if arch_subdir: + tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] - if self.pi.cross_dir() and self.vcver >= 14.0: + if self.vcver >= 14.0: path = 'Bin%s' % self.pi.current_dir(hidex86=True) - tools += [os.path.join(self.si.VCInstallDir, path)] + tools += [os.path.join(si.VCInstallDir, path)] else: - tools += [os.path.join(self.si.VCInstallDir, 'Bin')] + tools += [os.path.join(si.VCInstallDir, 'Bin')] return tools @@ -999,9 +1002,8 @@ class EnvironmentInfo: return [] arch_subdir = self.pi.current_dir(hidex86=True) - path = r'\MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) - return [os.path.join(self.si.ProgramFilesx86, path), - os.path.join(self.si.ProgramFiles, path)] + path = r'MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) + return [os.path.join(self.si.ProgramFilesx86, path)] @property def HTMLHelpWorkshop(self): @@ -1011,8 +1013,7 @@ class EnvironmentInfo: if self.vcver < 11.0: return [] - return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), - os.path.join(self.si.ProgramFiles, 'HTML Help Workshop')] + return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] @property def UCRTLibraries(self): -- cgit v1.2.1 From beddb52c98e81a191e04d61e020aca238d46b57b Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Thu, 19 May 2016 18:06:49 +0200 Subject: Update msvc9_support.py --- setuptools/msvc9_support.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 7cafd062..8cfba11e 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -4,6 +4,7 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) import os import collections import itertools +import platform import distutils.errors from setuptools.extern.six.moves import filterfalse @@ -57,6 +58,10 @@ def patch_for_specialized_compiler(): Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ + if platform.system() != 'Windows': + # Don't need to patch if not on Windows + return + if 'distutils' not in globals(): # The module isn't available to be patched return @@ -71,14 +76,14 @@ def patch_for_specialized_compiler(): msvc9compiler.find_vcvarsall = msvc9_find_vcvarsall unpatched['msvc9_query_vcvarsall'] = msvc9compiler.query_vcvarsall msvc9compiler.query_vcvarsall = msvc9_query_vcvarsall - except: + except Exception: pass try: # Patch distutils._msvccompiler._get_vc_env unpatched['msv14_get_vc_env'] = msvc14compiler._get_vc_env msvc14compiler._get_vc_env = msvc14_get_vc_env - except: + except Exception: pass @@ -193,7 +198,7 @@ def msvc14_get_vc_env(plat_spec): ------ environment: dict """ - # Try to get environement from vcvarsall.bat (Classical way) + # Try to get environment from vcvarsall.bat (Classical way) try: return unpatched['msv14_get_vc_env'](plat_spec) except distutils.errors.DistutilsPlatformError: @@ -278,7 +283,7 @@ class PlatformInfo: Return ------ subfolder: str - "\target" + '\target', or '' (see hidex86 parameter) """ return ( '' if (self.current_cpu == 'x86' and hidex86) else @@ -300,7 +305,7 @@ class PlatformInfo: Return ------ subfolder: str - "\current" + '\current', or '' (see hidex86 parameter) """ return ( '' if (self.target_cpu == 'x86' and hidex86) else @@ -315,20 +320,20 @@ class PlatformInfo: Parameters ---------- forcex86: bool - If cross compilation, return 'x86' as current architecture even - if current acritecture is not x86. + Use 'x86' as current architecture even if current acritecture is + not x86. Return ------ subfolder: str - "\current" if target architecture is current architecture, - "\current_target" if not. + '' if target architecture is current architecture, + '\current_target' if not. """ current = 'x86' if forcex86 else self.current_cpu - if self.target_cpu != current: - return self.target_dir().replace('\\', '\\%s_' % current) - else: - return '' + return ( + '' if self.target_cpu == current else + self.target_dir().replace('\\', '\\%s_' % current) + ) class RegistryInfo: -- cgit v1.2.1 From 6e11e32e4ac8afc30bf3a85d8e583ba55b1a1848 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 May 2016 13:29:58 -0400 Subject: Remove extra redundant work. Patch the module so behavior is the same on Unix systems even if not invoked. Tests still pass on Linux due to import protections. --- setuptools/msvc9_support.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 8cfba11e..d1f1a7a0 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -4,7 +4,6 @@ This module improve support for Microsoft Visual C++ compilers. (Windows Only) import os import collections import itertools -import platform import distutils.errors from setuptools.extern.six.moves import filterfalse @@ -58,10 +57,6 @@ def patch_for_specialized_compiler(): Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ - if platform.system() != 'Windows': - # Don't need to patch if not on Windows - return - if 'distutils' not in globals(): # The module isn't available to be patched return -- cgit v1.2.1 From 81cbfafddd92b39a6598ad3372e33eb28fadaf7d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 May 2016 13:32:58 -0400 Subject: Add missing C for consistency. --- setuptools/msvc9_support.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index d1f1a7a0..99582883 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -76,7 +76,7 @@ def patch_for_specialized_compiler(): try: # Patch distutils._msvccompiler._get_vc_env - unpatched['msv14_get_vc_env'] = msvc14compiler._get_vc_env + unpatched['msvc14_get_vc_env'] = msvc14compiler._get_vc_env msvc14compiler._get_vc_env = msvc14_get_vc_env except Exception: pass @@ -195,7 +195,7 @@ def msvc14_get_vc_env(plat_spec): """ # Try to get environment from vcvarsall.bat (Classical way) try: - return unpatched['msv14_get_vc_env'](plat_spec) + return unpatched['msvc14_get_vc_env'](plat_spec) except distutils.errors.DistutilsPlatformError: # Pass error Vcvarsall.bat is missing pass -- cgit v1.2.1 From b83cf1a68091b8de46f0ba19f1ce2f031d9cdc94 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 May 2016 13:41:32 -0400 Subject: Use underscore to separate words (or acronyms) for clarity (vc_ver). --- setuptools/msvc9_support.py | 120 ++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index 99582883..d4b7521a 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -457,28 +457,28 @@ class SystemInfo: ---------- registry_info: RegistryInfo "RegistryInfo" instance. - vcver: float + vc_ver: float Required Microsoft Visual C++ version. """ WinDir = safe_env['WinDir'] ProgramFiles = safe_env['ProgramFiles'] ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) - def __init__(self, registry_info, vcver=None): + def __init__(self, registry_info, vc_ver=None): self.ri = registry_info self.pi = self.ri.pi - if vcver: - self.vcver = vcver + if vc_ver: + self.vc_ver = vc_ver else: try: - self.vcver = self.find_availables_vcver()[-1] + self.vc_ver = self.find_available_vc_vers()[-1] except IndexError: err = 'No Microsoft Visual C++ version found' raise distutils.errors.DistutilsPlatformError(err) - def find_availables_vcver(self): + def find_available_vc_vers(self): """ - Find all availables Microsoft Visual C++ versions. + Find all available Microsoft Visual C++ versions. """ vckeys = (self.ri.vc, self.ri.vc_for_python) vsvers = [] @@ -511,11 +511,11 @@ class SystemInfo: Microsoft Visual Studio directory. """ # Default path - name = 'Microsoft Visual Studio %0.1f' % self.vcver + name = 'Microsoft Visual Studio %0.1f' % self.vc_ver default = os.path.join(self.ProgramFilesx86, name) # Try to get path from registry, if fail use default path - return self.ri.lookup(self.ri.vs, '%0.1f' % self.vcver) or default + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default @property def VCInstallDir(self): @@ -523,16 +523,16 @@ class SystemInfo: Microsoft Visual C++ directory. """ # Default path - default = r'Microsoft Visual Studio %0.1f\VC' % self.vcver + default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver guess_vc = os.path.join(self.ProgramFilesx86, default) # Try to get "VC++ for Python" path from registry as default path - path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vcver) + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) python_vc = self.ri.lookup(path, 'installdir') default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc # Try to get path from registry, if fail use default path - result = self.ri.lookup(self.ri.vc, '%0.1f' % self.vcver) or default_vc + result = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc if not os.path.isdir(result): msg = 'Microsoft Visual C++ directory not found' @@ -546,15 +546,15 @@ class SystemInfo: Microsoft Windows SDK versions. """ # Set Windows SDK versions for specified MSVC++ version - if self.vcver <= 9.0: + if self.vc_ver <= 9.0: return ('7.0', '6.1', '6.0a') - elif self.vcver == 10.0: + elif self.vc_ver == 10.0: return ('7.1', '7.0a') - elif self.vcver == 11.0: + elif self.vc_ver == 11.0: return ('8.0', '8.0a') - elif self.vcver == 12.0: + elif self.vc_ver == 12.0: return ('8.1', '8.1a') - elif self.vcver >= 14.0: + elif self.vc_ver >= 14.0: return ('10.0', '8.1') @property @@ -571,7 +571,7 @@ class SystemInfo: break if not sdkdir or not os.path.isdir(sdkdir): # Try to get "VC++ for Python" version from registry - path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vcver) + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) install_base = self.ri.lookup(path, 'installdir') if install_base: sdkdir = os.path.join(install_base, 'WinSDK') @@ -601,18 +601,18 @@ class SystemInfo: Microsoft Windows SDK executable directory. """ # Find WinSDK NetFx Tools registry dir name - if self.vcver <= 11.0: + if self.vc_ver <= 11.0: netfxver = 35 arch = '' else: netfxver = 40 - hidex86 = True if self.vcver <= 12.0 else False + hidex86 = True if self.vc_ver <= 12.0 else False arch = self.pi.current_dir(x64=True, hidex86=hidex86) fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) # liste all possibles registry paths regpaths = [] - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: for ver in self.NetFxSdkVersion: regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] @@ -631,7 +631,7 @@ class SystemInfo: """ Microsoft Visual F# directory. """ - path = r'%0.1f\Setup\F#' % self.vcver + path = r'%0.1f\Setup\F#' % self.vc_ver path = os.path.join(self.ri.visualstudio, path) return self.ri.lookup(path, 'productdir') or '' @@ -641,7 +641,7 @@ class SystemInfo: Microsoft Universal CRT SDK directory. """ # Set Kit Roots versions for specified MSVC++ version - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: vers = ('10', '81') else: vers = () @@ -660,7 +660,7 @@ class SystemInfo: Microsoft .NET Framework SDK versions. """ # Set FxSdk versions for specified MSVC++ version - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: return ('4.6.1', '4.6') else: return () @@ -726,14 +726,14 @@ class SystemInfo: ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' # Set .NET versions for specified MSVC++ version - if self.vcver >= 12.0: + if self.vc_ver >= 12.0: frameworkver = (ver, 'v4.0') - elif self.vcver >= 10.0: + elif self.vc_ver >= 10.0: frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5') - elif self.vcver == 9.0: + elif self.vc_ver == 9.0: frameworkver = ('v3.5', 'v2.0.50727') - if self.vcver == 8.0: + if self.vc_ver == 8.0: frameworkver = ('v3.0', 'v2.0.50727') return frameworkver @@ -752,24 +752,24 @@ class EnvironmentInfo: ---------- arch: str Target architecture. - vcver: float + vc_ver: float Required Microsoft Visual C++ version. If not set, autodetect the last version. - vcvermin: float + vc_min_ver: float Minimum Microsoft Visual C++ version. """ - def __init__(self, arch, vcver=None, vcvermin=None): + def __init__(self, arch, vc_ver=None, vc_min_ver=None): self.pi = PlatformInfo(arch) self.ri = RegistryInfo(self.pi) - self.si = SystemInfo(self.ri, vcver) + self.si = SystemInfo(self.ri, vc_ver) - if vcvermin: - if self.vcver < vcvermin: + if vc_min_ver: + if self.vc_ver < vc_min_ver: err = 'No suitable Microsoft Visual C++ version found' raise distutils.errors.DistutilsPlatformError(err) @property - def vcver(self): + def vc_ver(self): """ Microsoft Visual C++ version. """ @@ -782,7 +782,7 @@ class EnvironmentInfo: """ paths = [r'Common7\IDE', r'Common7\Tools'] - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] paths += [r'Team Tools\Performance Tools'] @@ -806,7 +806,7 @@ class EnvironmentInfo: arch_subdir = self.pi.target_dir(hidex86=True) paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: paths += [r'Lib\store%s' % arch_subdir] return [os.path.join(self.si.VCInstallDir, path) for path in paths] @@ -816,7 +816,7 @@ class EnvironmentInfo: """ Microsoft Visual C++ store references Libraries """ - if self.vcver < 14.0: + if self.vc_ver < 14.0: return [] return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] @@ -828,12 +828,12 @@ class EnvironmentInfo: si = self.si tools = [os.path.join(si.VCInstallDir, 'VCPackages')] - forcex86 = True if self.vcver <= 10.0 else False + forcex86 = True if self.vc_ver <= 10.0 else False arch_subdir = self.pi.cross_dir(forcex86) if arch_subdir: tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: path = 'Bin%s' % self.pi.current_dir(hidex86=True) tools += [os.path.join(si.VCInstallDir, path)] @@ -847,7 +847,7 @@ class EnvironmentInfo: """ Microsoft Windows SDK Libraries """ - if self.vcver <= 10.0: + if self.vc_ver <= 10.0: arch_subdir = self.pi.target_dir(hidex86=True, x64=True) return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] @@ -864,11 +864,11 @@ class EnvironmentInfo: """ include = os.path.join(self.si.WindowsSdkDir, 'include') - if self.vcver <= 10.0: + if self.vc_ver <= 10.0: return [include, os.path.join(include, 'gl')] else: - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: sdkver = self._get_content_dirname(include) else: sdkver = '' @@ -884,13 +884,13 @@ class EnvironmentInfo: ref = os.path.join(self.si.WindowsSdkDir, 'References') libpath = [] - if self.vcver <= 9.0: + if self.vc_ver <= 9.0: libpath += self.OSLibraries - if self.vcver >= 11.0: + if self.vc_ver >= 11.0: libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] - if self.vcver >= 14.0: + if self.vc_ver >= 14.0: libpath += [ref, os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), os.path.join(ref, r'Windows.Foundation.' @@ -902,7 +902,7 @@ class EnvironmentInfo: os.path.join(self.si.WindowsSdkDir, r'ExtensionSDKs' r'\Microsoft.VCLibs\%0.1f\References' r'\CommonConfiguration\neutral' % - self.vcver)] + self.vc_ver)] return libpath @property @@ -911,14 +911,14 @@ class EnvironmentInfo: Microsoft Windows SDK Tools """ tools = [os.path.join(self.si.WindowsSdkDir, - 'Bin' if self.vcver <= 11.0 else r'Bin\x86')] + 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86')] if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) path = 'Bin%s' % arch_subdir tools += [os.path.join(self.si.WindowsSdkDir, path)] - if self.vcver == 10.0 or self.vcver == 11.0: + if self.vc_ver == 10.0 or self.vc_ver == 11.0: if self.pi.target_is_x86(): arch_subdir = '' else: @@ -936,7 +936,7 @@ class EnvironmentInfo: """ Microsoft Windows SDK Setup """ - if self.vcver > 9.0: + if self.vc_ver > 9.0: return [] return [os.path.join(self.si.WindowsSdkDir, 'Setup')] @@ -949,7 +949,7 @@ class EnvironmentInfo: pi = self.pi si = self.si - if self.vcver <= 10.0: + if self.vc_ver <= 10.0: include32 = True include64 = not pi.target_is_x86() and not pi.current_is_x86() else: @@ -970,7 +970,7 @@ class EnvironmentInfo: """ Microsoft .Net Framework SDK Libraries """ - if self.vcver < 14.0 or not self.si.NetFxSdkDir: + if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: return [] arch_subdir = self.pi.target_dir(x64=True) @@ -981,7 +981,7 @@ class EnvironmentInfo: """ Microsoft .Net Framework SDK Includes """ - if self.vcver < 14.0 or not self.si.NetFxSdkDir: + if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: return [] return [os.path.join(self.si.NetFxSdkDir, r'include\um')] @@ -998,11 +998,11 @@ class EnvironmentInfo: """ Microsoft Build Engine """ - if self.vcver < 12.0: + if self.vc_ver < 12.0: return [] arch_subdir = self.pi.current_dir(hidex86=True) - path = r'MSBuild\%0.1f\bin%s' % (self.vcver, arch_subdir) + path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir) return [os.path.join(self.si.ProgramFilesx86, path)] @property @@ -1010,7 +1010,7 @@ class EnvironmentInfo: """ Microsoft HTML Help Workshop """ - if self.vcver < 11.0: + if self.vc_ver < 11.0: return [] return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] @@ -1020,7 +1020,7 @@ class EnvironmentInfo: """ Microsoft Universal CRT Libraries """ - if self.vcver < 14.0: + if self.vc_ver < 14.0: return [] arch_subdir = self.pi.target_dir(x64=True) @@ -1033,7 +1033,7 @@ class EnvironmentInfo: """ Microsoft Universal CRT Include """ - if self.vcver < 14.0: + if self.vc_ver < 14.0: return [] include = os.path.join(self.si.UniversalCRTSdkDir, 'include') @@ -1045,7 +1045,7 @@ class EnvironmentInfo: """ Microsoft Visual F# """ - if self.vcver < 11.0 and self.vcver > 12.0: + if self.vc_ver < 11.0 and self.vc_ver > 12.0: return [] return self.si.FSharpInstallDir @@ -1057,7 +1057,7 @@ class EnvironmentInfo: """ arch_subdir = self.pi.target_dir(x64=True) vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' - vcruntime = vcruntime % (arch_subdir, self.vcver, self.vcver) + vcruntime = vcruntime % (arch_subdir, self.vc_ver, self.vc_ver) return os.path.join(self.si.VCInstallDir, vcruntime) def return_env(self, exists=True): -- cgit v1.2.1 From aa96da3cae9512054399bb99ed7ecd098f8d671e Mon Sep 17 00:00:00 2001 From: Michael Klich Date: Thu, 2 Jun 2016 22:02:30 +0100 Subject: Update link to Resource Management API --- docs/setuptools.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 57818281..807a2722 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -917,7 +917,7 @@ use its resource management API. See also `Accessing Package Resources`_ for a quick example of converting code that uses ``__file__`` to use ``pkg_resources`` instead. -.. _Resource Management API: http://peak.telecommunity.com/DevCenter/PythonEggs#resource-management +.. _Resource Management API: http://peak.telecommunity.com/DevCenter/PkgResources#resourcemanager-api .. _Accessing Package Resources: http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources -- cgit v1.2.1 From 16361e51b834153f2e0e96897ebf33163c18016b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 Jun 2016 15:14:16 -0400 Subject: Remove ARM launchers. Fixes #611. --- CHANGES.rst | 7 +++++++ msvc-build-launcher.cmd | 16 ---------------- setuptools/cli-arm-32.exe | Bin 69120 -> 0 bytes setuptools/command/easy_install.py | 3 --- setuptools/gui-arm-32.exe | Bin 69120 -> 0 bytes 5 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 setuptools/cli-arm-32.exe delete mode 100644 setuptools/gui-arm-32.exe diff --git a/CHANGES.rst b/CHANGES.rst index b763607c..f13e8c62 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v23.0.0 +------- + +* #611: Removed ARM executables for CLI and GUI script + launchers on Windows. If this was a feature you cared + about, please comment in the ticket. + v22.0.5 ------- diff --git a/msvc-build-launcher.cmd b/msvc-build-launcher.cmd index e54c4f6c..92da290e 100644 --- a/msvc-build-launcher.cmd +++ b/msvc-build-launcher.cmd @@ -35,21 +35,5 @@ if "%ERRORLEVEL%"=="0" ( echo Windows SDK 6.1 not found to build Windows 64-bit version ) -REM Windows RT ARM build requires both freeware -REM "Visual Studio Express 2012 for Windows 8" and -REM "Visual Studio Express 2012 for Windows Desktop" to be installed from -REM http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products -set PATH=%PATH_OLD% -set PATH=C:\Program Files\Microsoft Visual Studio 11.0\VC;%PATH% -set PATH=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC;%PATH% -call VCVARSALL x86_arm >nul 2>&1 -if "%ERRORLEVEL%"=="0" ( - echo Building Windows RT Version ... - cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" /D _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE launcher.c /O2 /link /MACHINE:ARM /SUBSYSTEM:CONSOLE /out:setuptools/cli-arm-32.exe - cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" /D _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE launcher.c /O2 /link /MACHINE:ARM /SUBSYSTEM:WINDOWS /out:setuptools/gui-arm-32.exe -) else ( - echo Visual Studio ^(Express^) 2012 not found to build Windows RT Version -) - set PATH=%PATH_OLD% diff --git a/setuptools/cli-arm-32.exe b/setuptools/cli-arm-32.exe deleted file mode 100644 index 2f40402d..00000000 Binary files a/setuptools/cli-arm-32.exe and /dev/null differ diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index ccc66cf7..9ca1554e 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -32,7 +32,6 @@ import zipfile import re import stat import random -import platform import textwrap import warnings import site @@ -2203,8 +2202,6 @@ def get_win_launcher(type): Returns the executable as a byte string. """ launcher_fn = '%s.exe' % type - if platform.machine().lower() == 'arm': - launcher_fn = launcher_fn.replace(".", "-arm.") if is_64bit(): launcher_fn = launcher_fn.replace(".", "-64.") else: diff --git a/setuptools/gui-arm-32.exe b/setuptools/gui-arm-32.exe deleted file mode 100644 index 537aff37..00000000 Binary files a/setuptools/gui-arm-32.exe and /dev/null differ -- cgit v1.2.1 From dac38d28df3cfa1afb4370e2303c87822f1ffb86 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 Jun 2016 15:16:24 -0400 Subject: Update changelog --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f13e8c62..a6455292 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,9 @@ v23.0.0 * #611: Removed ARM executables for CLI and GUI script launchers on Windows. If this was a feature you cared about, please comment in the ticket. +* #604: Removed docs building support. The project + now relies on documentation hosted at + https://setuptools.readthedocs.io/. v22.0.5 ------- -- cgit v1.2.1 From cb0a2aeb86953d6b3cf6f039bb887fb8c7e8d1e9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 Jun 2016 15:18:34 -0400 Subject: =?UTF-8?q?Bump=20version:=2022.0.5=20=E2=86=92=2023.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 0d1ea3ac..e21e1c2b 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 22.0.5 +current_version = 23.0.0 commit = True tag = True diff --git a/setup.py b/setup.py index 98b046f8..8642ea34 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="22.0.5", + version="23.0.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From a73ca4fdcd1ba386159eb6ceb66d40af674a034c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 Jun 2016 15:18:34 -0400 Subject: Added tag v23.0.0 for changeset 8664c631bf3a --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 86954cd8..d43b8997 100644 --- a/.hgtags +++ b/.hgtags @@ -272,3 +272,4 @@ f5c4923b0400d61f67699c2d54388878f9e0c8bd v22.0.1 efee7d74a8478c0d08c801fb520e41b6e04d0dda v22.0.3 77b20c09b04775cc936ab5d16cbc46ff05fc7080 v22.0.4 d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 +8664c631bf3a817a7deba86c13b67eccc1f81091 v23.0.0 -- cgit v1.2.1 From 9abc044eeb7c6c4acc72c0f537cc249e817b83e3 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 14 Jun 2016 20:01:41 +0200 Subject: Some fixes - Fix issue #593 - Fix some forgotten "vcver" => "vc_ver" renames in previous commit. - Add comment on why I didn't followed PEP8 for some variables names. --- setuptools/msvc9_support.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py index d4b7521a..d1ed2edc 100644 --- a/setuptools/msvc9_support.py +++ b/setuptools/msvc9_support.py @@ -202,7 +202,7 @@ def msvc14_get_vc_env(plat_spec): # If error, try to set environment directly try: - return EnvironmentInfo(plat_spec, vcvermin=14.0).return_env() + return EnvironmentInfo(plat_spec, vc_ver_min=14.0).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, 14.0) raise @@ -237,7 +237,7 @@ def _augment_exception(exc, version, arch=''): message += ' Get it with "Microsoft Windows SDK 7.1": ' message += msdownload % 8279 - exc.args[0] = message + exc.args = (message, ) class PlatformInfo: @@ -460,6 +460,8 @@ class SystemInfo: vc_ver: float Required Microsoft Visual C++ version. """ + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparaison. WinDir = safe_env['WinDir'] ProgramFiles = safe_env['ProgramFiles'] ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) @@ -481,7 +483,7 @@ class SystemInfo: Find all available Microsoft Visual C++ versions. """ vckeys = (self.ri.vc, self.ri.vc_for_python) - vsvers = [] + vc_vers = [] for hkey in self.ri.HKEYS: for key in vckeys: try: @@ -492,18 +494,18 @@ class SystemInfo: for i in range(values): try: ver = float(winreg.EnumValue(bkey, i)[0]) - if ver not in vsvers: - vsvers.append(ver) + if ver not in vc_vers: + vc_vers.append(ver) except ValueError: pass for i in range(subkeys): try: ver = float(winreg.EnumKey(bkey, i)) - if ver not in vsvers: - vsvers.append(ver) + if ver not in vc_vers: + vc_vers.append(ver) except ValueError: pass - return sorted(vsvers) + return sorted(vc_vers) @property def VSInstallDir(self): @@ -527,18 +529,18 @@ class SystemInfo: guess_vc = os.path.join(self.ProgramFilesx86, default) # Try to get "VC++ for Python" path from registry as default path - path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) - python_vc = self.ri.lookup(path, 'installdir') + reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + python_vc = self.ri.lookup(reg_path, 'installdir') default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc # Try to get path from registry, if fail use default path - result = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc + path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc - if not os.path.isdir(result): + if not os.path.isdir(path): msg = 'Microsoft Visual C++ directory not found' raise distutils.errors.DistutilsPlatformError(msg) - return result + return path @property def WindowsSdkVersion(self): @@ -758,6 +760,8 @@ class EnvironmentInfo: vc_min_ver: float Minimum Microsoft Visual C++ version. """ + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparaison. def __init__(self, arch, vc_ver=None, vc_min_ver=None): self.pi = PlatformInfo(arch) self.ri = RegistryInfo(self.pi) @@ -773,7 +777,7 @@ class EnvironmentInfo: """ Microsoft Visual C++ version. """ - return self.si.vcver + return self.si.vc_ver @property def VSTools(self): @@ -1101,7 +1105,7 @@ class EnvironmentInfo: self.FSharp], exists), ) - if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): + if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist): env['py_vcruntime_redist'] = self.VCRuntimeRedist return env -- cgit v1.2.1 From c986bbf7359a5db45a6e96689729b2f04d2bd3e8 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Thu, 23 Jun 2016 14:37:14 +0000 Subject: Add Gitter badge --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 4f833ecf..1c6a0b86 100755 --- a/README.rst +++ b/README.rst @@ -37,6 +37,10 @@ Powershell command. Start up Powershell and paste this command:: > (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - +.. image:: https://badges.gitter.im/pypa/setuptools.svg + :alt: Join the chat at https://gitter.im/pypa/setuptools + :target: https://gitter.im/pypa/setuptools?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge + You must start the Powershell with Administrative privileges or you may choose to install a user-local installation:: -- cgit v1.2.1 From 3cee4d8f79a20f1d67b194ee383dddd826695e0d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jun 2016 09:44:52 -0400 Subject: Nicer indentation --- setuptools/command/egg_info.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 8e1502a5..829f03f9 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -52,8 +52,10 @@ class egg_info(Command): ] boolean_options = ['tag-date', 'tag-svn-revision'] - negative_opt = {'no-svn-revision': 'tag-svn-revision', - 'no-date': 'tag-date'} + negative_opt = { + 'no-svn-revision': 'tag-svn-revision', + 'no-date': 'tag-date', + } def initialize_options(self): self.egg_name = None -- cgit v1.2.1 From 542a921bb6943feb1c90874ba5151c94622b52b3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jun 2016 10:17:41 -0400 Subject: Mark tag_svn_revision as deprecated. Ref #619. --- CHANGES.rst | 6 ++++++ setuptools/command/egg_info.py | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a6455292..055d7aa9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v23.1.0 +------- + +* #619: Deprecated ``tag_svn_revision`` distribution + option. + v23.0.0 ------- diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 829f03f9..5183eedc 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -199,6 +199,10 @@ class egg_info(Command): if self.tag_build: version += self.tag_build if self.tag_svn_revision: + warnings.warn( + "tag_svn_revision is deprecated and will not be honored " + "in a future release" + ) version += '-r%s' % self.get_svn_revision() if self.tag_date: version += time.strftime("-%Y%m%d") -- cgit v1.2.1 From 6c1cc9fa3acfa32571e5e10c76c8a87f993adb8e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jun 2016 10:17:52 -0400 Subject: =?UTF-8?q?Bump=20version:=2023.0.0=20=E2=86=92=2023.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index e21e1c2b..d371955f 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 23.0.0 +current_version = 23.1.0 commit = True tag = True diff --git a/setup.py b/setup.py index 8642ea34..558bd345 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="23.0.0", + version="23.1.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From ce3ad9e1927faff667ad2bf2fec43a0e5254bdf4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Jun 2016 10:17:52 -0400 Subject: Added tag v23.1.0 for changeset 6c74559c732c --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index d43b8997..b27122d0 100644 --- a/.hgtags +++ b/.hgtags @@ -273,3 +273,4 @@ efee7d74a8478c0d08c801fb520e41b6e04d0dda v22.0.3 77b20c09b04775cc936ab5d16cbc46ff05fc7080 v22.0.4 d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 8664c631bf3a817a7deba86c13b67eccc1f81091 v23.0.0 +6c74559c732c56f61b465d613458ec1a930884b6 v23.1.0 -- cgit v1.2.1 From 5d8c17c312e4586ea7a37673f147f9e9622617c7 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sat, 25 Jun 2016 14:38:28 +0200 Subject: readme: update links to documentation --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 1c6a0b86..d8d8a6e5 100755 --- a/README.rst +++ b/README.rst @@ -182,10 +182,10 @@ them there, so this reference list can be updated. If you have working, the `setuptools bug tracker`_. .. _setuptools bug tracker: https://github.com/pypa/setuptools/issues -.. _The Internal Structure of Python Eggs: https://pythonhosted.org/setuptools/formats.html -.. _The setuptools Developer's Guide: https://pythonhosted.org/setuptools/setuptools.html -.. _The pkg_resources API reference: https://pythonhosted.org/setuptools/pkg_resources.html -.. _The EasyInstall user's guide and reference manual: https://pythonhosted.org/setuptools/easy_install.html +.. _The Internal Structure of Python Eggs: https://setuptools.readthedocs.io/en/latest/formats.html +.. _The setuptools Developer's Guide: https://setuptools.readthedocs.io/en/latest/developer-guide.html +.. _The pkg_resources API reference: https://setuptools.readthedocs.io/en/latest/pkg_resources.html +.. _The EasyInstall user's guide and reference manual: https://setuptools.readthedocs.io/en/latest/easy_install.html .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/ -- cgit v1.2.1 From bba4b3975c834655df6e97f89ed099ab21594d1c Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 26 Jun 2016 01:33:23 +0700 Subject: Fix misspellings --- CHANGES.rst | 4 ++-- docs/easy_install.txt | 4 ++-- pkg_resources/__init__.py | 2 +- pkg_resources/_vendor/packaging/specifiers.py | 2 +- pkg_resources/_vendor/pyparsing.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 055d7aa9..482059b3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -129,7 +129,7 @@ v20.8.0 v20.7.0 ------- -* Refactored extra enviroment marker processing +* Refactored extra environment marker processing in WorkingSet. * Issue #533: Fixed intermittent test failures. * Issue #536: In msvc9_support, trap additional exceptions @@ -216,7 +216,7 @@ v20.6.0 20.2.1 ------ -* Issue #499: Restore compatiblity for legacy versions +* Issue #499: Restore compatibility for legacy versions by bumping to packaging 16.4. 20.2 diff --git a/docs/easy_install.txt b/docs/easy_install.txt index a66909b1..591589fb 100644 --- a/docs/easy_install.txt +++ b/docs/easy_install.txt @@ -577,7 +577,7 @@ activated or deactivated. As a result, if you are using EasyInstall to upgrade an existing package, or to install a package with the same name as an existing package, EasyInstall will warn you of the conflict. (This is an improvement over ``setup.py -install``, becuase the ``distutils`` just install new packages on top of old +install``, because the ``distutils`` just install new packages on top of old ones, possibly combining two unrelated packages or leaving behind modules that have been deleted in the newer version of the package.) @@ -606,7 +606,7 @@ can be safely installed as a zipfile, and then acts on its analysis. (Previous versions would not install a package as a zipfile unless you used the ``--zip-ok`` option.) -The current analysis approach is fairly conservative; it currenly looks for: +The current analysis approach is fairly conservative; it currently looks for: * Any use of the ``__file__`` or ``__path__`` variables (which should be replaced with ``pkg_resources`` API calls) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2eab8230..15cb93fd 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1367,7 +1367,7 @@ def get_default_cache(): return os.path.join(dirname, 'Python-Eggs') else: raise RuntimeError( - "Please set the PYTHON_EGG_CACHE enviroment variable" + "Please set the PYTHON_EGG_CACHE environment variable" ) def safe_name(name): diff --git a/pkg_resources/_vendor/packaging/specifiers.py b/pkg_resources/_vendor/packaging/specifiers.py index 7f5a76cf..9b6353f0 100644 --- a/pkg_resources/_vendor/packaging/specifiers.py +++ b/pkg_resources/_vendor/packaging/specifiers.py @@ -198,7 +198,7 @@ class _IndividualSpecifier(BaseSpecifier): (prereleases or self.prereleases)): found_prereleases.append(version) # Either this is not a prerelease, or we should have been - # accepting prereleases from the begining. + # accepting prereleases from the beginning. else: yielded = True yield version diff --git a/pkg_resources/_vendor/pyparsing.py b/pkg_resources/_vendor/pyparsing.py index 3e02dbee..2284cadc 100644 --- a/pkg_resources/_vendor/pyparsing.py +++ b/pkg_resources/_vendor/pyparsing.py @@ -1112,7 +1112,7 @@ class ParserElement(object): (see L{I{parseWithTabs}}) - define your parse action using the full C{(s,loc,toks)} signature, and reference the input string using the parse action's C{s} argument - - explictly expand the tabs in your input string before calling + - explicitly expand the tabs in your input string before calling C{parseString} """ ParserElement.resetCache() -- cgit v1.2.1 From 45597769a552c9bf9f7e425a6a9d3c26064841cf Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Sat, 25 Jun 2016 18:41:51 +0200 Subject: Fix test_all_site_dirs on Windows. --- setuptools/tests/test_easy_install.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index fd06b6ef..c903d2bd 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -30,7 +30,7 @@ import setuptools.command.easy_install as ei from setuptools.command.easy_install import PthDistributions from setuptools.command import easy_install as easy_install_pkg from setuptools.dist import Distribution -from pkg_resources import working_set +from pkg_resources import normalize_path, working_set from pkg_resources import Distribution as PRDistribution import setuptools.tests.server import pkg_resources @@ -123,9 +123,10 @@ class TestEasyInstallTest: get_site_dirs should always return site dirs reported by site.getsitepackages. """ - mock_gsp = lambda: ['/setuptools/test/site-packages'] + path = normalize_path('/setuptools/test/site-packages') + mock_gsp = lambda: [path] monkeypatch.setattr(site, 'getsitepackages', mock_gsp, raising=False) - assert '/setuptools/test/site-packages' in ei.get_site_dirs() + assert path in ei.get_site_dirs() def test_all_site_dirs_works_without_getsitepackages(self, monkeypatch): monkeypatch.delattr(site, 'getsitepackages', raising=False) -- cgit v1.2.1 From 6baa4a140f77d17e131febb3d76d0dae3ca4dfc9 Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Sat, 25 Jun 2016 22:44:05 +0200 Subject: Remove CommandSpec.from_param test using sys.executable. It was based on the assumption that CommandSpec.from_param(sys.executable) should always return a 1-element list. Since from_param parses the argument and sys.executable may contain unquoted spaces, this simply can't be guaranteed. --- setuptools/tests/test_easy_install.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index c903d2bd..7e77e819 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -591,15 +591,6 @@ class TestCommandSpec: assert len(cmd) == 2 assert '"' not in cmd.as_header() - def test_sys_executable(self): - """ - CommandSpec.from_string(sys.executable) should contain just that param. - """ - writer = ei.ScriptWriter.best() - cmd = writer.command_spec_class.from_string(sys.executable) - assert len(cmd) == 1 - assert cmd[0] == sys.executable - class TestWindowsScriptWriter: def test_header(self): -- cgit v1.2.1 From 9c3cdde0ddbae5684bfec874e6d8ca239ffb6379 Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Sun, 26 Jun 2016 00:53:14 +0200 Subject: Split up single TestScriptHeader test into multiple tests. --- setuptools/tests/test_easy_install.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 7e77e819..02f27059 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -528,29 +528,32 @@ def make_trivial_sdist(dist_path, setup_py): dist.addfile(setup_py_file, fileobj=setup_py_bytes) +@pytest.mark.skipif( + sys.platform.startswith('java') and ei.is_sh(sys.executable), + reason="Test cannot run under java when executable is sh" +) class TestScriptHeader: non_ascii_exe = '/Users/José/bin/python' exe_with_spaces = r'C:\Program Files\Python33\python.exe' - @pytest.mark.skipif( - sys.platform.startswith('java') and ei.is_sh(sys.executable), - reason="Test cannot run under java when executable is sh" - ) def test_get_script_header(self): expected = '#!%s\n' % ei.nt_quote_arg(os.path.normpath(sys.executable)) actual = ei.ScriptWriter.get_script_header('#!/usr/local/bin/python') assert actual == expected + def test_get_script_header_args(self): expected = '#!%s -x\n' % ei.nt_quote_arg(os.path.normpath (sys.executable)) actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python -x') assert actual == expected + def test_get_script_header_non_ascii_exe(self): actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python', executable=self.non_ascii_exe) expected = '#!%s -x\n' % self.non_ascii_exe assert actual == expected + def test_get_script_header_exe_with_spaces(self): actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python', executable='"'+self.exe_with_spaces+'"') expected = '#!"%s"\n' % self.exe_with_spaces -- cgit v1.2.1 From 6f79ca2b726b2b92d007d09fb855f19ede5a1921 Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Wed, 22 Jun 2016 17:42:32 +0200 Subject: Test quoting of shebang lines. See issues #188 and #398; on Windows, the launcher executables support shebangs that use quotes to escape spaces, so these should be used when necessary. On the other hand, Unix systems don't support shebangs with quotes so they should never have quotes. --- setuptools/tests/test_install_scripts.py | 88 ++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 setuptools/tests/test_install_scripts.py diff --git a/setuptools/tests/test_install_scripts.py b/setuptools/tests/test_install_scripts.py new file mode 100644 index 00000000..7393241f --- /dev/null +++ b/setuptools/tests/test_install_scripts.py @@ -0,0 +1,88 @@ +"""install_scripts tests +""" + +import io +import sys + +import pytest + +from setuptools.command.install_scripts import install_scripts +from setuptools.dist import Distribution +from . import contexts + + +class TestInstallScripts: + settings = dict( + name='foo', + entry_points={'console_scripts': ['foo=foo:foo']}, + version='0.0', + ) + unix_exe = '/usr/dummy-test-path/local/bin/python' + unix_spaces_exe = '/usr/bin/env dummy-test-python' + win32_exe = 'C:\\Dummy Test Path\\Program Files\\Python 3.3\\python.exe' + + def _run_install_scripts(self, install_dir, executable=None): + dist = Distribution(self.settings) + dist.script_name = 'setup.py' + cmd = install_scripts(dist) + cmd.install_dir = install_dir + if executable is not None: + bs = cmd.get_finalized_command('build_scripts') + bs.executable = executable + cmd.ensure_finalized() + with contexts.quiet(): + cmd.run() + + @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') + def test_sys_executable_escaping_unix(self, tmpdir, monkeypatch): + """ + Ensure that shebang is not quoted on Unix when getting the Python exe + from sys.executable. + """ + expected = '#!%s\n' % self.unix_exe + monkeypatch.setattr('sys.executable', self.unix_exe) + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir)) + with io.open(str(tmpdir.join('foo')), 'r') as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only') + def test_sys_executable_escaping_win32(self, tmpdir, monkeypatch): + """ + Ensure that shebang is quoted on Windows when getting the Python exe + from sys.executable and it contains a space. + """ + expected = '#!"%s"\n' % self.win32_exe + monkeypatch.setattr('sys.executable', self.win32_exe) + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir)) + with io.open(str(tmpdir.join('foo-script.py')), 'r') as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only') + def test_executable_with_spaces_escaping_unix(self, tmpdir): + """ + Ensure that shebang on Unix is not quoted, even when a value with spaces + is specified using --executable. + """ + expected = '#!%s\n' % self.unix_spaces_exe + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir), self.unix_spaces_exe) + with io.open(str(tmpdir.join('foo')), 'r') as f: + actual = f.readline() + assert actual == expected + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only') + def test_executable_arg_escaping_win32(self, tmpdir): + """ + Ensure that shebang on Windows is quoted when getting a path with spaces + from --executable, that is itself properly quoted. + """ + expected = '#!"%s"\n' % self.win32_exe + with tmpdir.as_cwd(): + self._run_install_scripts(str(tmpdir), '"' + self.win32_exe + '"') + with io.open(str(tmpdir.join('foo-script.py')), 'r') as f: + actual = f.readline() + assert actual == expected -- cgit v1.2.1 From 3132833570c90d52f6c2a422506732e82d772cdd Mon Sep 17 00:00:00 2001 From: Felix Krull Date: Sun, 26 Jun 2016 01:48:30 +0200 Subject: Ensure shebang lines are correctly quoted if sys.executable contains spaces. Fixes issue #398. This only special-cases sys.executable; if the --executable parameter is used, paths with spaces have to be quoted there explicitly. While this change also applies to Unix platforms, if sys.executable contains spaces on Unix, any shebang lines created with it aren't going to work either way, whether they are quoted or not. --- setuptools/command/easy_install.py | 11 ++++++++++- setuptools/command/install_scripts.py | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 9ca1554e..d63fb529 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1985,9 +1985,18 @@ class CommandSpec(list): def as_header(self): return self._render(self + list(self.options)) + @staticmethod + def _strip_quotes(item): + _QUOTES = '"\'' + for q in _QUOTES: + if item.startswith(q) and item.endswith(q): + return item[1:-1] + return item + @staticmethod def _render(items): - cmdline = subprocess.list2cmdline(items) + cmdline = subprocess.list2cmdline( + CommandSpec._strip_quotes(item.strip()) for item in items) return '#!' + cmdline + '\n' # For pbr compat; will be removed in a future version. diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index be66cb22..16234273 100755 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -1,6 +1,7 @@ from distutils import log import distutils.command.install_scripts as orig import os +import sys from pkg_resources import Distribution, PathMetadata, ensure_directory @@ -37,6 +38,10 @@ class install_scripts(orig.install_scripts): if is_wininst: exec_param = "python.exe" writer = ei.WindowsScriptWriter + if exec_param == sys.executable: + # In case the path to the Python executable contains a space, wrap + # it so it's not split up. + exec_param = [exec_param] # resolve the writer to the environment writer = writer.best() cmd = writer.command_spec_class.best().from_param(exec_param) -- cgit v1.2.1 From 4720c481c62d5cc0a40dd212a32c641125be8e9c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 29 Jun 2016 10:22:12 +0200 Subject: Don't use deprecated 'U' flag to read manifest The universal newlines mode ('U' flag) is deprecated since Python 3.4. It only replaces "\r\n" with "\n", but it doesn't split lines at "\r" (Mac newline). In practice, the flag was useless, the sdist.read_manifest() method already uses line.strip() and so removes newline characters. --- setuptools/command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 6640d4e3..f200b946 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -179,7 +179,7 @@ class sdist(orig.sdist): distribution. """ log.info("reading manifest file '%s'", self.manifest) - manifest = open(self.manifest, 'rbU') + manifest = open(self.manifest, 'rb') for line in manifest: # The manifest must contain UTF-8. See #303. if six.PY3: -- cgit v1.2.1 From cb88c78645a37ff81efa440f82e3df234bf2c9cf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 10:17:26 -0400 Subject: Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 055d7aa9..a529514d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v23.2.0 +------- + +* #623: Remove used of deprecated 'U' flag when reading + manifests. + v23.1.0 ------- -- cgit v1.2.1 From 54325a753f688285c71a6db0a062116e6dc6976c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:06:19 -0400 Subject: Rename msvc9_support to simply msvc. --- CHANGES.rst | 2 + setuptools/extension.py | 4 +- setuptools/msvc.py | 1175 ++++++++++++++++++++++++++++++++ setuptools/msvc9_support.py | 1174 ------------------------------- setuptools/tests/test_msvc9compiler.py | 2 +- 5 files changed, 1180 insertions(+), 1177 deletions(-) create mode 100644 setuptools/msvc.py delete mode 100644 setuptools/msvc9_support.py diff --git a/CHANGES.rst b/CHANGES.rst index 4ee60250..7a1be732 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,8 @@ Next * Pull Request #174: Add more aggressive support for Windows SDK in msvc9compiler patch. +* Renamed ``setuptools.msvc9_support`` to + ``setuptools.msvc``. v21.2.0 ------- diff --git a/setuptools/extension.py b/setuptools/extension.py index d10609b6..b9d68178 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -8,11 +8,11 @@ import distutils.extension from setuptools.extern.six.moves import map from .dist import _get_unpatched -from . import msvc9_support +from . import msvc _Extension = _get_unpatched(distutils.core.Extension) -msvc9_support.patch_for_specialized_compiler() +msvc.patch_for_specialized_compiler() def _have_cython(): """ diff --git a/setuptools/msvc.py b/setuptools/msvc.py new file mode 100644 index 00000000..63031ac2 --- /dev/null +++ b/setuptools/msvc.py @@ -0,0 +1,1175 @@ +""" +This module adds improved support for Microsoft Visual C++ compilers. +""" + +import os +import collections +import itertools +import distutils.errors +from setuptools.extern.six.moves import filterfalse + +try: + from setuptools.extern.six.moves import winreg + safe_env = os.environ +except ImportError: + """ + Mock winreg and environ so the module can be imported + on this platform. + """ + class winreg: + HKEY_USERS = None + HKEY_CURRENT_USER = None + HKEY_LOCAL_MACHINE = None + HKEY_CLASSES_ROOT = None + safe_env = collections.defaultdict(lambda: '') + + +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 + + +unpatched = dict() + + +def patch_for_specialized_compiler(): + """ + Patch functions in distutils to use standalone Microsoft Visual C++ + compilers. + + Known supported compilers: + -------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) + + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + """ + if 'distutils' not in globals(): + # The module isn't available to be patched + return + + 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 Exception: + 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 Exception: + pass + + +def msvc9_find_vcvarsall(version): + """ + Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone + compiler build for Python (VCForPython). Fall back to original behavior + when the standalone compiler is not available. + + Redirect the path of "vcvarsall.bat". + + Known supported compilers + ------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + + Parameters + ---------- + version: float + Required Microsoft Visual C++ version. + + Return + ------ + vcvarsall.bat path: str + """ + Reg = msvc9compiler.Reg + VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' + key = VC_BASE % ('', version) + try: + # Per-user installs register the compiler path here + productdir = Reg.get_value(key, "installdir") + except KeyError: + try: + # All-user installs on a 64-bit system register here + key = VC_BASE % ('Wow6432Node\\', version) + productdir = Reg.get_value(key, "installdir") + except KeyError: + productdir = None + + if productdir: + vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat") + if os.path.isfile(vcvarsall): + return vcvarsall + + return unpatched['msvc9_find_vcvarsall'](version) + + +def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): + """ + Patched "distutils.msvc9compiler.query_vcvarsall" for support standalones + compilers. + + Set environment without use of "vcvarsall.bat". + + Known supported compilers + ------------------------- + Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) + + Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + + Parameters + ---------- + ver: float + Required Microsoft Visual C++ version. + arch: str + Target architecture. + + Return + ------ + environment: dict + """ + # Try to get environement from vcvarsall.bat (Classical way) + try: + return unpatched['msvc9_query_vcvarsall'](ver, arch, *args, **kwargs) + except distutils.errors.DistutilsPlatformError: + # Pass error if Vcvarsall.bat is missing + pass + except ValueError: + # Pass error if environment not set after executing vcvarsall.bat + pass + + # If error, try to set environment directly + try: + return EnvironmentInfo(arch, ver).return_env() + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, ver, arch) + raise + + +def msvc14_get_vc_env(plat_spec): + """ + Patched "distutils._msvccompiler._get_vc_env" for support standalones + compilers. + + Set environment without use of "vcvarsall.bat". + + Known supported compilers + ------------------------- + Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + + Parameters + ---------- + plat_spec: str + Target architecture. + + Return + ------ + environment: dict + """ + # Try to get environment from vcvarsall.bat (Classical way) + try: + return unpatched['msvc14_get_vc_env'](plat_spec) + except distutils.errors.DistutilsPlatformError: + # Pass error Vcvarsall.bat is missing + pass + + # If error, try to set environment directly + try: + return EnvironmentInfo(plat_spec, vc_ver_min=14.0).return_env() + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, 14.0) + raise + + +def _augment_exception(exc, version, arch=''): + """ + Add details to the exception message to help guide the user + as to what action will resolve it. + """ + # Error if MSVC++ directory not found or environment not set + message = exc.args[0] + + if "vcvarsall" in message.lower() or "visual c" in message.lower(): + # Special error message if MSVC++ not installed + message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ + (version, message) + msdownload = r'www.microsoft.com/download/details.aspx?id=%d' + if version == 9.0: + if arch.lower().find('ia64') > -1: + # For VC++ 9.0, if IA64 support is needed, redirect user + # to Windows SDK 7.0 + message += ' Get it with "Microsoft Windows SDK 7.0": ' + message += msdownload % 3138 + else: + # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : + # This redirection link is maintained by Microsoft. + # Contact vspython@microsoft.com if it needs updating. + message += r' Get it from http://aka.ms/vcpython27' + elif version == 10.0: + # For VC++ 10.0 Redirect user to Windows SDK 7.1 + message += ' Get it with "Microsoft Windows SDK 7.1": ' + message += msdownload % 8279 + + exc.args = (message, ) + + +class PlatformInfo: + """ + Current and Target Architectures informations. + + Parameters + ---------- + arch: str + Target architecture. + """ + current_cpu = safe_env['processor_architecture'].lower() + + def __init__(self, arch): + self.arch = arch.lower().replace('x64', 'amd64') + + @property + def target_cpu(self): + return self.arch[self.arch.find('_') + 1:] + + def target_is_x86(self): + return self.target_cpu == 'x86' + + def current_is_x86(self): + return self.current_cpu == 'x86' + + def current_dir(self, hidex86=False, x64=False): + """ + Current platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + subfolder: str + '\target', or '' (see hidex86 parameter) + """ + return ( + '' if (self.current_cpu == 'x86' and hidex86) else + r'\x64' if (self.current_cpu == 'amd64' and x64) else + r'\%s' % self.current_cpu + ) + + def target_dir(self, hidex86=False, x64=False): + """ + Target platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + subfolder: str + '\current', or '' (see hidex86 parameter) + """ + return ( + '' if (self.target_cpu == 'x86' and hidex86) else + r'\x64' if (self.target_cpu == 'amd64' and x64) else + r'\%s' % self.target_cpu + ) + + def cross_dir(self, forcex86=False): + """ + Cross platform specific subfolder. + + Parameters + ---------- + forcex86: bool + Use 'x86' as current architecture even if current acritecture is + not x86. + + Return + ------ + subfolder: str + '' if target architecture is current architecture, + '\current_target' if not. + """ + current = 'x86' if forcex86 else self.current_cpu + return ( + '' if self.target_cpu == current else + self.target_dir().replace('\\', '\\%s_' % current) + ) + + +class RegistryInfo: + """ + Microsoft Visual Studio related registry informations. + + Parameters + ---------- + platform_info: PlatformInfo + "PlatformInfo" instance. + """ + HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + + def __init__(self, platform_info): + self.pi = platform_info + + @property + def microsoft(self): + """ + Microsoft software registry key. + """ + return os.path.join( + 'Software', + '' if self.pi.current_is_x86() else 'Wow6432Node', + 'Microsoft', + ) + + @property + def visualstudio(self): + """ + Microsoft Visual Studio root registry key. + """ + return os.path.join(self.microsoft, r'VisualStudio') + + @property + def sxs(self): + """ + Microsoft Visual Studio SxS registry key. + """ + return os.path.join(self.visualstudio, 'SxS') + + @property + def vc(self): + """ + Microsoft Visual C++ VC7 registry key. + """ + return os.path.join(self.sxs, 'VC7') + + @property + def vs(self): + """ + Microsoft Visual Studio VS7 registry key. + """ + return os.path.join(self.sxs, 'VS7') + + @property + def vc_for_python(self): + """ + Microsoft Visual C++ for Python registry key. + """ + path = r'DevDiv\VCForPython' + return os.path.join(self.microsoft, path) + + @property + def microsoft_sdk(self): + """ + Microsoft SDK registry key. + """ + return os.path.join(self.microsoft, 'Microsoft SDKs') + + @property + def windows_sdk(self): + """ + Microsoft Windows/Platform SDK registry key. + """ + return os.path.join(self.microsoft_sdk, 'Windows') + + @property + def netfx_sdk(self): + """ + Microsoft .NET Framework SDK registry key. + """ + return os.path.join(self.microsoft_sdk, 'NETFXSDK') + + @property + def windows_kits_roots(self): + """ + Microsoft Windows Kits Roots registry key. + """ + return os.path.join(self.microsoft, r'Windows Kits\Installed Roots') + + def lookup(self, key, name): + """ + Look for values in registry. + + Parameters + ---------- + key: str + Registry key path where look. + name: str + Value name to find. + + Return + ------ + str: value + """ + for hkey in self.HKEYS: + try: + bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except FileNotFoundError: + continue + try: + return winreg.QueryValueEx(bkey, name)[0] + except FileNotFoundError: + pass + + +class SystemInfo: + """ + Microsoft Windows and Visual Studio related system inormations. + + Parameters + ---------- + registry_info: RegistryInfo + "RegistryInfo" instance. + vc_ver: float + Required Microsoft Visual C++ version. + """ + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparaison. + WinDir = safe_env['WinDir'] + ProgramFiles = safe_env['ProgramFiles'] + ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) + + def __init__(self, registry_info, vc_ver=None): + self.ri = registry_info + self.pi = self.ri.pi + if vc_ver: + self.vc_ver = vc_ver + else: + try: + self.vc_ver = self.find_available_vc_vers()[-1] + except IndexError: + err = 'No Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + def find_available_vc_vers(self): + """ + Find all available Microsoft Visual C++ versions. + """ + vckeys = (self.ri.vc, self.ri.vc_for_python) + vc_vers = [] + for hkey in self.ri.HKEYS: + for key in vckeys: + try: + bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except FileNotFoundError: + continue + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + try: + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vc_vers: + vc_vers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vc_vers: + vc_vers.append(ver) + except ValueError: + pass + return sorted(vc_vers) + + @property + def VSInstallDir(self): + """ + Microsoft Visual Studio directory. + """ + # Default path + name = 'Microsoft Visual Studio %0.1f' % self.vc_ver + default = os.path.join(self.ProgramFilesx86, name) + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default + + @property + def VCInstallDir(self): + """ + Microsoft Visual C++ directory. + """ + # Default path + default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver + guess_vc = os.path.join(self.ProgramFilesx86, default) + + # Try to get "VC++ for Python" path from registry as default path + reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + python_vc = self.ri.lookup(reg_path, 'installdir') + default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc + + # Try to get path from registry, if fail use default path + path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc + + if not os.path.isdir(path): + msg = 'Microsoft Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) + + return path + + @property + def WindowsSdkVersion(self): + """ + Microsoft Windows SDK versions. + """ + # Set Windows SDK versions for specified MSVC++ version + if self.vc_ver <= 9.0: + return ('7.0', '6.1', '6.0a') + elif self.vc_ver == 10.0: + return ('7.1', '7.0a') + elif self.vc_ver == 11.0: + return ('8.0', '8.0a') + elif self.vc_ver == 12.0: + return ('8.1', '8.1a') + elif self.vc_ver >= 14.0: + return ('10.0', '8.1') + + @property + def WindowsSdkDir(self): + """ + Microsoft Windows SDK directory. + """ + sdkdir = '' + for ver in self.WindowsSdkVersion: + # Try to get it from registry + loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: + break + if not sdkdir or not os.path.isdir(sdkdir): + # Try to get "VC++ for Python" version from registry + path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + install_base = self.ri.lookup(path, 'installdir') + if install_base: + sdkdir = os.path.join(install_base, 'WinSDK') + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default new path + for ver in self.WindowsSdkVersion: + intver = ver[:ver.rfind('.')] + path = r'Microsoft SDKs\Windows Kits\%s' % (intver) + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir or not os.path.isdir(sdkdir): + # If fail, use default old path + for ver in self.WindowsSdkVersion: + path = r'Microsoft SDKs\Windows\v%s' % ver + d = os.path.join(self.ProgramFiles, path) + if os.path.isdir(d): + sdkdir = d + if not sdkdir: + # If fail, use Platform SDK + sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') + return sdkdir + + @property + def WindowsSDKExecutablePath(self): + """ + Microsoft Windows SDK executable directory. + """ + # Find WinSDK NetFx Tools registry dir name + if self.vc_ver <= 11.0: + netfxver = 35 + arch = '' + else: + netfxver = 40 + hidex86 = True if self.vc_ver <= 12.0 else False + arch = self.pi.current_dir(x64=True, hidex86=hidex86) + fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) + + # liste all possibles registry paths + regpaths = [] + if self.vc_ver >= 14.0: + for ver in self.NetFxSdkVersion: + regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] + + for ver in self.WindowsSdkVersion: + regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] + + # Return installation folder from the more recent path + for path in regpaths: + execpath = self.ri.lookup(path, 'installationfolder') + if execpath: + break + return execpath + + @property + def FSharpInstallDir(self): + """ + Microsoft Visual F# directory. + """ + path = r'%0.1f\Setup\F#' % self.vc_ver + path = os.path.join(self.ri.visualstudio, path) + return self.ri.lookup(path, 'productdir') or '' + + @property + def UniversalCRTSdkDir(self): + """ + Microsoft Universal CRT SDK directory. + """ + # Set Kit Roots versions for specified MSVC++ version + if self.vc_ver >= 14.0: + vers = ('10', '81') + else: + vers = () + + # Find path of the more recent Kit + for ver in vers: + sdkdir = self.ri.lookup(self.ri.windows_kits_roots, + 'kitsroot%s' % ver) + if sdkdir: + break + return sdkdir or '' + + @property + def NetFxSdkVersion(self): + """ + Microsoft .NET Framework SDK versions. + """ + # Set FxSdk versions for specified MSVC++ version + if self.vc_ver >= 14.0: + return ('4.6.1', '4.6') + else: + return () + + @property + def NetFxSdkDir(self): + """ + Microsoft .NET Framework SDK directory. + """ + for ver in self.NetFxSdkVersion: + loc = os.path.join(self.ri.netfx_sdk, ver) + sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') + if sdkdir: + break + return sdkdir or '' + + @property + def FrameworkDir32(self): + """ + Microsoft .NET Framework 32bit directory. + """ + # Default path + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw + + @property + def FrameworkDir64(self): + """ + Microsoft .NET Framework 64bit directory. + """ + # Default path + guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw + + @property + def FrameworkVersion32(self): + """ + Microsoft .NET Framework 32bit versions. + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self): + """ + Microsoft .NET Framework 64bit versions. + """ + return self._find_dot_net_versions(64) + + def _find_dot_net_versions(self, bits=32): + """ + Find Microsoft .NET Framework versions. + + Parameters + ---------- + bits: int + Platform number of bits: 32 or 64. + """ + # Find actual .NET version + ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' + + # Set .NET versions for specified MSVC++ version + if self.vc_ver >= 12.0: + frameworkver = (ver, 'v4.0') + elif self.vc_ver >= 10.0: + frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver, + 'v3.5') + elif self.vc_ver == 9.0: + frameworkver = ('v3.5', 'v2.0.50727') + if self.vc_ver == 8.0: + frameworkver = ('v3.0', 'v2.0.50727') + return frameworkver + + +class EnvironmentInfo: + """ + Return environment variables for specified Microsoft Visual C++ version + and platform : Lib, Include, Path and libpath. + + This function is compatible with Microsoft Visual C++ 9.0 to 14.0. + + Script created by analysing Microsoft environment configuration files like + "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... + + Parameters + ---------- + arch: str + Target architecture. + vc_ver: float + Required Microsoft Visual C++ version. If not set, autodetect the last + version. + vc_min_ver: float + Minimum Microsoft Visual C++ version. + """ + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparaison. + def __init__(self, arch, vc_ver=None, vc_min_ver=None): + self.pi = PlatformInfo(arch) + self.ri = RegistryInfo(self.pi) + self.si = SystemInfo(self.ri, vc_ver) + + if vc_min_ver: + if self.vc_ver < vc_min_ver: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + @property + def vc_ver(self): + """ + Microsoft Visual C++ version. + """ + return self.si.vc_ver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + + if self.vc_ver >= 14.0: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] + paths += [r'Team Tools\Performance Tools'] + paths += [r'Team Tools\Performance Tools%s' % arch_subdir] + + return [os.path.join(self.si.VSInstallDir, path) for path in paths] + + @property + def VCIncludes(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Includes + """ + return [os.path.join(self.si.VCInstallDir, 'Include'), + os.path.join(self.si.VCInstallDir, 'ATLMFC\Include')] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries + """ + arch_subdir = self.pi.target_dir(hidex86=True) + paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] + + if self.vc_ver >= 14.0: + paths += [r'Lib\store%s' % arch_subdir] + + return [os.path.join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries + """ + if self.vc_ver < 14.0: + return [] + return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools + """ + si = self.si + tools = [os.path.join(si.VCInstallDir, 'VCPackages')] + + forcex86 = True if self.vc_ver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + if arch_subdir: + tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] + + if self.vc_ver >= 14.0: + path = 'Bin%s' % self.pi.current_dir(hidex86=True) + tools += [os.path.join(si.VCInstallDir, path)] + + else: + tools += [os.path.join(si.VCInstallDir, 'Bin')] + + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries + """ + if self.vc_ver <= 10.0: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] + + else: + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.WindowsSdkDir, 'lib') + libver = self._get_content_dirname(lib) + return [os.path.join(lib, r'%sum%s' % (libver, arch_subdir))] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include + """ + include = os.path.join(self.si.WindowsSdkDir, 'include') + + if self.vc_ver <= 10.0: + return [include, os.path.join(include, 'gl')] + + else: + if self.vc_ver >= 14.0: + sdkver = self._get_content_dirname(include) + else: + sdkver = '' + return [os.path.join(include, '%sshared' % sdkver), + os.path.join(include, '%sum' % sdkver), + os.path.join(include, '%swinrt' % sdkver)] + + @property + def OSLibpath(self): + """ + Microsoft Windows SDK Libraries Paths + """ + ref = os.path.join(self.si.WindowsSdkDir, 'References') + libpath = [] + + if self.vc_ver <= 9.0: + libpath += self.OSLibraries + + if self.vc_ver >= 11.0: + libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] + + if self.vc_ver >= 14.0: + libpath += [ref, + os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), + os.path.join(ref, r'Windows.Foundation.' + r'UniversalApiContract\1.0.0.0'), + os.path.join(ref, r'Windows.Foundation.' + r'FoundationContract\1.0.0.0'), + os.path.join(ref, r'Windows.Networking.Connectivity.' + r'WwanContract\1.0.0.0'), + os.path.join(self.si.WindowsSdkDir, r'ExtensionSDKs' + r'\Microsoft.VCLibs\%0.1f\References' + r'\CommonConfiguration\neutral' % + self.vc_ver)] + return libpath + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools + """ + tools = [os.path.join(self.si.WindowsSdkDir, + 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86')] + + if not self.pi.current_is_x86(): + arch_subdir = self.pi.current_dir(x64=True) + path = 'Bin%s' % arch_subdir + tools += [os.path.join(self.si.WindowsSdkDir, path)] + + if self.vc_ver == 10.0 or self.vc_ver == 11.0: + if self.pi.target_is_x86(): + arch_subdir = '' + else: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir + tools += [os.path.join(self.si.WindowsSdkDir, path)] + + if self.si.WindowsSDKExecutablePath: + tools += [self.si.WindowsSDKExecutablePath] + + return tools + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup + """ + if self.vc_ver > 9.0: + return [] + + return [os.path.join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools + """ + pi = self.pi + si = self.si + + if self.vc_ver <= 10.0: + include32 = True + include64 = not pi.target_is_x86() and not pi.current_is_x86() + else: + include32 = pi.target_is_x86() or pi.current_is_x86() + include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' + + tools = [] + if include32: + tools += [os.path.join(si.FrameworkDir32, ver) + for ver in si.FrameworkVersion32] + if include64: + tools += [os.path.join(si.FrameworkDir64, ver) + for ver in si.FrameworkVersion64] + return tools + + @property + def NetFxSDKLibraries(self): + """ + Microsoft .Net Framework SDK Libraries + """ + if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] + + @property + def NetFxSDKIncludes(self): + """ + Microsoft .Net Framework SDK Includes + """ + if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + return [os.path.join(self.si.NetFxSdkDir, r'include\um')] + + @property + def VsTDb(self): + """ + Microsoft Visual Studio Team System Database + """ + return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine + """ + if self.vc_ver < 12.0: + return [] + + arch_subdir = self.pi.current_dir(hidex86=True) + path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir) + return [os.path.join(self.si.ProgramFilesx86, path)] + + @property + def HTMLHelpWorkshop(self): + """ + Microsoft HTML Help Workshop + """ + if self.vc_ver < 11.0: + return [] + + return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] + + @property + def UCRTLibraries(self): + """ + Microsoft Universal CRT Libraries + """ + if self.vc_ver < 14.0: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') + ucrtver = self._get_content_dirname(lib) + return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] + + @property + def UCRTIncludes(self): + """ + Microsoft Universal CRT Include + """ + if self.vc_ver < 14.0: + return [] + + include = os.path.join(self.si.UniversalCRTSdkDir, 'include') + ucrtver = self._get_content_dirname(include) + return [os.path.join(include, '%sucrt' % ucrtver)] + + @property + def FSharp(self): + """ + Microsoft Visual F# + """ + if self.vc_ver < 11.0 and self.vc_ver > 12.0: + return [] + + return self.si.FSharpInstallDir + + @property + def VCRuntimeRedist(self): + """ + Microsoft Visual C++ runtime redistribuable dll + """ + arch_subdir = self.pi.target_dir(x64=True) + vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + vcruntime = vcruntime % (arch_subdir, self.vc_ver, self.vc_ver) + return os.path.join(self.si.VCInstallDir, vcruntime) + + def return_env(self, exists=True): + """ + Return environment dict. + + Parameters + ---------- + exists: bool + It True, only return existing paths. + """ + env = dict( + include=self._build_paths('include', + [self.VCIncludes, + self.OSIncludes, + self.UCRTIncludes, + self.NetFxSDKIncludes], + exists), + lib=self._build_paths('lib', + [self.VCLibraries, + self.OSLibraries, + self.FxTools, + self.UCRTLibraries, + self.NetFxSDKLibraries], + exists), + libpath=self._build_paths('libpath', + [self.VCLibraries, + self.FxTools, + self.VCStoreRefs, + self.OSLibpath], + exists), + path=self._build_paths('path', + [self.VCTools, + self.VSTools, + self.VsTDb, + self.SdkTools, + self.SdkSetup, + self.FxTools, + self.MSBuild, + self.HTMLHelpWorkshop, + self.FSharp], + exists), + ) + if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist): + env['py_vcruntime_redist'] = self.VCRuntimeRedist + return env + + def _build_paths(self, name, spec_path_lists, exists): + """ + Given an environment variable name and specified paths, + return a pathsep-separated string of paths containing + unique, extant, directories from those paths and from + the environment variable. Raise an error if no paths + are resolved. + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = os.environ.get(name, '').split(os.pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(os.path.isdir, paths)) if exists else paths + if not extant_paths: + msg = "%s environment variable is empty" % name.upper() + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = self._unique_everseen(extant_paths) + return os.pathsep.join(unique_paths) + + # from Python docs + def _unique_everseen(self, iterable, key=None): + """ + List unique elements, preserving order. + Remember all elements ever seen. + + _unique_everseen('AAAABBBCCDAABBB') --> A B C D + + _unique_everseen('ABBCcAD', str.lower) --> A B C D + """ + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element + + def _get_content_dirname(self, path): + """ + Return name of the first dir in path or '' if no dir found. + + Parameters + ---------- + path: str + Path where search dir. + + Return + ------ + foldername: str + "name\" or "" + """ + try: + name = os.listdir(path) + if name: + return '%s\\' % name[0] + return '' + except FileNotFoundError: + return '' diff --git a/setuptools/msvc9_support.py b/setuptools/msvc9_support.py deleted file mode 100644 index d1ed2edc..00000000 --- a/setuptools/msvc9_support.py +++ /dev/null @@ -1,1174 +0,0 @@ -""" -This module improve support for Microsoft Visual C++ compilers. (Windows Only) -""" -import os -import collections -import itertools -import distutils.errors -from setuptools.extern.six.moves import filterfalse - -try: - from setuptools.extern.six.moves import winreg - safe_env = os.environ -except ImportError: - """ - Mock winreg and environ so the module can be imported - on this platform. - """ - class winreg: - HKEY_USERS = None - HKEY_CURRENT_USER = None - HKEY_LOCAL_MACHINE = None - HKEY_CLASSES_ROOT = None - safe_env = collections.defaultdict(lambda: '') - - -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 - - -unpatched = dict() - - -def patch_for_specialized_compiler(): - """ - Patch functions in distutils to use standalone Microsoft Visual C++ - compilers. - - Known supported compilers: - -------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) - - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) - - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) - """ - if 'distutils' not in globals(): - # The module isn't available to be patched - return - - 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 Exception: - 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 Exception: - pass - - -def msvc9_find_vcvarsall(version): - """ - Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone - compiler build for Python (VCForPython). Fall back to original behavior - when the standalone compiler is not available. - - Redirect the path of "vcvarsall.bat". - - Known supported compilers - ------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) - - Parameters - ---------- - version: float - Required Microsoft Visual C++ version. - - Return - ------ - vcvarsall.bat path: str - """ - Reg = msvc9compiler.Reg - VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' - key = VC_BASE % ('', version) - try: - # Per-user installs register the compiler path here - productdir = Reg.get_value(key, "installdir") - except KeyError: - try: - # All-user installs on a 64-bit system register here - key = VC_BASE % ('Wow6432Node\\', version) - productdir = Reg.get_value(key, "installdir") - except KeyError: - productdir = None - - if productdir: - vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat") - if os.path.isfile(vcvarsall): - return vcvarsall - - return unpatched['msvc9_find_vcvarsall'](version) - - -def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): - """ - Patched "distutils.msvc9compiler.query_vcvarsall" for support standalones - compilers. - - Set environment without use of "vcvarsall.bat". - - Known supported compilers - ------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) - - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) - - Parameters - ---------- - ver: float - Required Microsoft Visual C++ version. - arch: str - Target architecture. - - Return - ------ - environment: dict - """ - # Try to get environement from vcvarsall.bat (Classical way) - try: - return unpatched['msvc9_query_vcvarsall'](ver, arch, *args, **kwargs) - except distutils.errors.DistutilsPlatformError: - # Pass error if Vcvarsall.bat is missing - pass - except ValueError: - # Pass error if environment not set after executing vcvarsall.bat - pass - - # If error, try to set environment directly - try: - return EnvironmentInfo(arch, ver).return_env() - except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, ver, arch) - raise - - -def msvc14_get_vc_env(plat_spec): - """ - Patched "distutils._msvccompiler._get_vc_env" for support standalones - compilers. - - Set environment without use of "vcvarsall.bat". - - Known supported compilers - ------------------------- - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) - - Parameters - ---------- - plat_spec: str - Target architecture. - - Return - ------ - environment: dict - """ - # Try to get environment from vcvarsall.bat (Classical way) - try: - return unpatched['msvc14_get_vc_env'](plat_spec) - except distutils.errors.DistutilsPlatformError: - # Pass error Vcvarsall.bat is missing - pass - - # If error, try to set environment directly - try: - return EnvironmentInfo(plat_spec, vc_ver_min=14.0).return_env() - except distutils.errors.DistutilsPlatformError as exc: - _augment_exception(exc, 14.0) - raise - - -def _augment_exception(exc, version, arch=''): - """ - Add details to the exception message to help guide the user - as to what action will resolve it. - """ - # Error if MSVC++ directory not found or environment not set - message = exc.args[0] - - if "vcvarsall" in message.lower() or "visual c" in message.lower(): - # Special error message if MSVC++ not installed - message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ - (version, message) - msdownload = r'www.microsoft.com/download/details.aspx?id=%d' - if version == 9.0: - if arch.lower().find('ia64') > -1: - # For VC++ 9.0, if IA64 support is needed, redirect user - # to Windows SDK 7.0 - message += ' Get it with "Microsoft Windows SDK 7.0": ' - message += msdownload % 3138 - else: - # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : - # This redirection link is maintained by Microsoft. - # Contact vspython@microsoft.com if it needs updating. - message += r' Get it from http://aka.ms/vcpython27' - elif version == 10.0: - # For VC++ 10.0 Redirect user to Windows SDK 7.1 - message += ' Get it with "Microsoft Windows SDK 7.1": ' - message += msdownload % 8279 - - exc.args = (message, ) - - -class PlatformInfo: - """ - Current and Target Architectures informations. - - Parameters - ---------- - arch: str - Target architecture. - """ - current_cpu = safe_env['processor_architecture'].lower() - - def __init__(self, arch): - self.arch = arch.lower().replace('x64', 'amd64') - - @property - def target_cpu(self): - return self.arch[self.arch.find('_') + 1:] - - def target_is_x86(self): - return self.target_cpu == 'x86' - - def current_is_x86(self): - return self.current_cpu == 'x86' - - def current_dir(self, hidex86=False, x64=False): - """ - Current platform specific subfolder. - - Parameters - ---------- - hidex86: bool - return '' and not '\x86' if architecture is x86. - x64: bool - return '\x64' and not '\amd64' if architecture is amd64. - - Return - ------ - subfolder: str - '\target', or '' (see hidex86 parameter) - """ - return ( - '' if (self.current_cpu == 'x86' and hidex86) else - r'\x64' if (self.current_cpu == 'amd64' and x64) else - r'\%s' % self.current_cpu - ) - - def target_dir(self, hidex86=False, x64=False): - """ - Target platform specific subfolder. - - Parameters - ---------- - hidex86: bool - return '' and not '\x86' if architecture is x86. - x64: bool - return '\x64' and not '\amd64' if architecture is amd64. - - Return - ------ - subfolder: str - '\current', or '' (see hidex86 parameter) - """ - return ( - '' if (self.target_cpu == 'x86' and hidex86) else - r'\x64' if (self.target_cpu == 'amd64' and x64) else - r'\%s' % self.target_cpu - ) - - def cross_dir(self, forcex86=False): - """ - Cross platform specific subfolder. - - Parameters - ---------- - forcex86: bool - Use 'x86' as current architecture even if current acritecture is - not x86. - - Return - ------ - subfolder: str - '' if target architecture is current architecture, - '\current_target' if not. - """ - current = 'x86' if forcex86 else self.current_cpu - return ( - '' if self.target_cpu == current else - self.target_dir().replace('\\', '\\%s_' % current) - ) - - -class RegistryInfo: - """ - Microsoft Visual Studio related registry informations. - - Parameters - ---------- - platform_info: PlatformInfo - "PlatformInfo" instance. - """ - HKEYS = (winreg.HKEY_USERS, - winreg.HKEY_CURRENT_USER, - winreg.HKEY_LOCAL_MACHINE, - winreg.HKEY_CLASSES_ROOT) - - def __init__(self, platform_info): - self.pi = platform_info - - @property - def microsoft(self): - """ - Microsoft software registry key. - """ - return os.path.join( - 'Software', - '' if self.pi.current_is_x86() else 'Wow6432Node', - 'Microsoft', - ) - - @property - def visualstudio(self): - """ - Microsoft Visual Studio root registry key. - """ - return os.path.join(self.microsoft, r'VisualStudio') - - @property - def sxs(self): - """ - Microsoft Visual Studio SxS registry key. - """ - return os.path.join(self.visualstudio, 'SxS') - - @property - def vc(self): - """ - Microsoft Visual C++ VC7 registry key. - """ - return os.path.join(self.sxs, 'VC7') - - @property - def vs(self): - """ - Microsoft Visual Studio VS7 registry key. - """ - return os.path.join(self.sxs, 'VS7') - - @property - def vc_for_python(self): - """ - Microsoft Visual C++ for Python registry key. - """ - path = r'DevDiv\VCForPython' - return os.path.join(self.microsoft, path) - - @property - def microsoft_sdk(self): - """ - Microsoft SDK registry key. - """ - return os.path.join(self.microsoft, 'Microsoft SDKs') - - @property - def windows_sdk(self): - """ - Microsoft Windows/Platform SDK registry key. - """ - return os.path.join(self.microsoft_sdk, 'Windows') - - @property - def netfx_sdk(self): - """ - Microsoft .NET Framework SDK registry key. - """ - return os.path.join(self.microsoft_sdk, 'NETFXSDK') - - @property - def windows_kits_roots(self): - """ - Microsoft Windows Kits Roots registry key. - """ - return os.path.join(self.microsoft, r'Windows Kits\Installed Roots') - - def lookup(self, key, name): - """ - Look for values in registry. - - Parameters - ---------- - key: str - Registry key path where look. - name: str - Value name to find. - - Return - ------ - str: value - """ - for hkey in self.HKEYS: - try: - bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) - except FileNotFoundError: - continue - try: - return winreg.QueryValueEx(bkey, name)[0] - except FileNotFoundError: - pass - - -class SystemInfo: - """ - Microsoft Windows and Visual Studio related system inormations. - - Parameters - ---------- - registry_info: RegistryInfo - "RegistryInfo" instance. - vc_ver: float - Required Microsoft Visual C++ version. - """ - # Variables and properties in this class use originals CamelCase variables - # names from Microsoft source files for more easy comparaison. - WinDir = safe_env['WinDir'] - ProgramFiles = safe_env['ProgramFiles'] - ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) - - def __init__(self, registry_info, vc_ver=None): - self.ri = registry_info - self.pi = self.ri.pi - if vc_ver: - self.vc_ver = vc_ver - else: - try: - self.vc_ver = self.find_available_vc_vers()[-1] - except IndexError: - err = 'No Microsoft Visual C++ version found' - raise distutils.errors.DistutilsPlatformError(err) - - def find_available_vc_vers(self): - """ - Find all available Microsoft Visual C++ versions. - """ - vckeys = (self.ri.vc, self.ri.vc_for_python) - vc_vers = [] - for hkey in self.ri.HKEYS: - for key in vckeys: - try: - bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) - except FileNotFoundError: - continue - subkeys, values, _ = winreg.QueryInfoKey(bkey) - for i in range(values): - try: - ver = float(winreg.EnumValue(bkey, i)[0]) - if ver not in vc_vers: - vc_vers.append(ver) - except ValueError: - pass - for i in range(subkeys): - try: - ver = float(winreg.EnumKey(bkey, i)) - if ver not in vc_vers: - vc_vers.append(ver) - except ValueError: - pass - return sorted(vc_vers) - - @property - def VSInstallDir(self): - """ - Microsoft Visual Studio directory. - """ - # Default path - name = 'Microsoft Visual Studio %0.1f' % self.vc_ver - default = os.path.join(self.ProgramFilesx86, name) - - # Try to get path from registry, if fail use default path - return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default - - @property - def VCInstallDir(self): - """ - Microsoft Visual C++ directory. - """ - # Default path - default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver - guess_vc = os.path.join(self.ProgramFilesx86, default) - - # Try to get "VC++ for Python" path from registry as default path - reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) - python_vc = self.ri.lookup(reg_path, 'installdir') - default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc - - # Try to get path from registry, if fail use default path - path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc - - if not os.path.isdir(path): - msg = 'Microsoft Visual C++ directory not found' - raise distutils.errors.DistutilsPlatformError(msg) - - return path - - @property - def WindowsSdkVersion(self): - """ - Microsoft Windows SDK versions. - """ - # Set Windows SDK versions for specified MSVC++ version - if self.vc_ver <= 9.0: - return ('7.0', '6.1', '6.0a') - elif self.vc_ver == 10.0: - return ('7.1', '7.0a') - elif self.vc_ver == 11.0: - return ('8.0', '8.0a') - elif self.vc_ver == 12.0: - return ('8.1', '8.1a') - elif self.vc_ver >= 14.0: - return ('10.0', '8.1') - - @property - def WindowsSdkDir(self): - """ - Microsoft Windows SDK directory. - """ - sdkdir = '' - for ver in self.WindowsSdkVersion: - # Try to get it from registry - loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) - sdkdir = self.ri.lookup(loc, 'installationfolder') - if sdkdir: - break - if not sdkdir or not os.path.isdir(sdkdir): - # Try to get "VC++ for Python" version from registry - path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) - install_base = self.ri.lookup(path, 'installdir') - if install_base: - sdkdir = os.path.join(install_base, 'WinSDK') - if not sdkdir or not os.path.isdir(sdkdir): - # If fail, use default new path - for ver in self.WindowsSdkVersion: - intver = ver[:ver.rfind('.')] - path = r'Microsoft SDKs\Windows Kits\%s' % (intver) - d = os.path.join(self.ProgramFiles, path) - if os.path.isdir(d): - sdkdir = d - if not sdkdir or not os.path.isdir(sdkdir): - # If fail, use default old path - for ver in self.WindowsSdkVersion: - path = r'Microsoft SDKs\Windows\v%s' % ver - d = os.path.join(self.ProgramFiles, path) - if os.path.isdir(d): - sdkdir = d - if not sdkdir: - # If fail, use Platform SDK - sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') - return sdkdir - - @property - def WindowsSDKExecutablePath(self): - """ - Microsoft Windows SDK executable directory. - """ - # Find WinSDK NetFx Tools registry dir name - if self.vc_ver <= 11.0: - netfxver = 35 - arch = '' - else: - netfxver = 40 - hidex86 = True if self.vc_ver <= 12.0 else False - arch = self.pi.current_dir(x64=True, hidex86=hidex86) - fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) - - # liste all possibles registry paths - regpaths = [] - if self.vc_ver >= 14.0: - for ver in self.NetFxSdkVersion: - regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)] - - for ver in self.WindowsSdkVersion: - regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)] - - # Return installation folder from the more recent path - for path in regpaths: - execpath = self.ri.lookup(path, 'installationfolder') - if execpath: - break - return execpath - - @property - def FSharpInstallDir(self): - """ - Microsoft Visual F# directory. - """ - path = r'%0.1f\Setup\F#' % self.vc_ver - path = os.path.join(self.ri.visualstudio, path) - return self.ri.lookup(path, 'productdir') or '' - - @property - def UniversalCRTSdkDir(self): - """ - Microsoft Universal CRT SDK directory. - """ - # Set Kit Roots versions for specified MSVC++ version - if self.vc_ver >= 14.0: - vers = ('10', '81') - else: - vers = () - - # Find path of the more recent Kit - for ver in vers: - sdkdir = self.ri.lookup(self.ri.windows_kits_roots, - 'kitsroot%s' % ver) - if sdkdir: - break - return sdkdir or '' - - @property - def NetFxSdkVersion(self): - """ - Microsoft .NET Framework SDK versions. - """ - # Set FxSdk versions for specified MSVC++ version - if self.vc_ver >= 14.0: - return ('4.6.1', '4.6') - else: - return () - - @property - def NetFxSdkDir(self): - """ - Microsoft .NET Framework SDK directory. - """ - for ver in self.NetFxSdkVersion: - loc = os.path.join(self.ri.netfx_sdk, ver) - sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') - if sdkdir: - break - return sdkdir or '' - - @property - def FrameworkDir32(self): - """ - Microsoft .NET Framework 32bit directory. - """ - # Default path - guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework') - - # Try to get path from registry, if fail use default path - return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw - - @property - def FrameworkDir64(self): - """ - Microsoft .NET Framework 64bit directory. - """ - # Default path - guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64') - - # Try to get path from registry, if fail use default path - return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw - - @property - def FrameworkVersion32(self): - """ - Microsoft .NET Framework 32bit versions. - """ - return self._find_dot_net_versions(32) - - @property - def FrameworkVersion64(self): - """ - Microsoft .NET Framework 64bit versions. - """ - return self._find_dot_net_versions(64) - - def _find_dot_net_versions(self, bits=32): - """ - Find Microsoft .NET Framework versions. - - Parameters - ---------- - bits: int - Platform number of bits: 32 or 64. - """ - # Find actual .NET version - ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' - - # Set .NET versions for specified MSVC++ version - if self.vc_ver >= 12.0: - frameworkver = (ver, 'v4.0') - elif self.vc_ver >= 10.0: - frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver, - 'v3.5') - elif self.vc_ver == 9.0: - frameworkver = ('v3.5', 'v2.0.50727') - if self.vc_ver == 8.0: - frameworkver = ('v3.0', 'v2.0.50727') - return frameworkver - - -class EnvironmentInfo: - """ - Return environment variables for specified Microsoft Visual C++ version - and platform : Lib, Include, Path and libpath. - - This function is compatible with Microsoft Visual C++ 9.0 to 14.0. - - Script created by analysing Microsoft environment configuration files like - "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... - - Parameters - ---------- - arch: str - Target architecture. - vc_ver: float - Required Microsoft Visual C++ version. If not set, autodetect the last - version. - vc_min_ver: float - Minimum Microsoft Visual C++ version. - """ - # Variables and properties in this class use originals CamelCase variables - # names from Microsoft source files for more easy comparaison. - def __init__(self, arch, vc_ver=None, vc_min_ver=None): - self.pi = PlatformInfo(arch) - self.ri = RegistryInfo(self.pi) - self.si = SystemInfo(self.ri, vc_ver) - - if vc_min_ver: - if self.vc_ver < vc_min_ver: - err = 'No suitable Microsoft Visual C++ version found' - raise distutils.errors.DistutilsPlatformError(err) - - @property - def vc_ver(self): - """ - Microsoft Visual C++ version. - """ - return self.si.vc_ver - - @property - def VSTools(self): - """ - Microsoft Visual Studio Tools - """ - paths = [r'Common7\IDE', r'Common7\Tools'] - - if self.vc_ver >= 14.0: - arch_subdir = self.pi.current_dir(hidex86=True, x64=True) - paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] - paths += [r'Team Tools\Performance Tools'] - paths += [r'Team Tools\Performance Tools%s' % arch_subdir] - - return [os.path.join(self.si.VSInstallDir, path) for path in paths] - - @property - def VCIncludes(self): - """ - Microsoft Visual C++ & Microsoft Foundation Class Includes - """ - return [os.path.join(self.si.VCInstallDir, 'Include'), - os.path.join(self.si.VCInstallDir, 'ATLMFC\Include')] - - @property - def VCLibraries(self): - """ - Microsoft Visual C++ & Microsoft Foundation Class Libraries - """ - arch_subdir = self.pi.target_dir(hidex86=True) - paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] - - if self.vc_ver >= 14.0: - paths += [r'Lib\store%s' % arch_subdir] - - return [os.path.join(self.si.VCInstallDir, path) for path in paths] - - @property - def VCStoreRefs(self): - """ - Microsoft Visual C++ store references Libraries - """ - if self.vc_ver < 14.0: - return [] - return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')] - - @property - def VCTools(self): - """ - Microsoft Visual C++ Tools - """ - si = self.si - tools = [os.path.join(si.VCInstallDir, 'VCPackages')] - - forcex86 = True if self.vc_ver <= 10.0 else False - arch_subdir = self.pi.cross_dir(forcex86) - if arch_subdir: - tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] - - if self.vc_ver >= 14.0: - path = 'Bin%s' % self.pi.current_dir(hidex86=True) - tools += [os.path.join(si.VCInstallDir, path)] - - else: - tools += [os.path.join(si.VCInstallDir, 'Bin')] - - return tools - - @property - def OSLibraries(self): - """ - Microsoft Windows SDK Libraries - """ - if self.vc_ver <= 10.0: - arch_subdir = self.pi.target_dir(hidex86=True, x64=True) - return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] - - else: - arch_subdir = self.pi.target_dir(x64=True) - lib = os.path.join(self.si.WindowsSdkDir, 'lib') - libver = self._get_content_dirname(lib) - return [os.path.join(lib, r'%sum%s' % (libver, arch_subdir))] - - @property - def OSIncludes(self): - """ - Microsoft Windows SDK Include - """ - include = os.path.join(self.si.WindowsSdkDir, 'include') - - if self.vc_ver <= 10.0: - return [include, os.path.join(include, 'gl')] - - else: - if self.vc_ver >= 14.0: - sdkver = self._get_content_dirname(include) - else: - sdkver = '' - return [os.path.join(include, '%sshared' % sdkver), - os.path.join(include, '%sum' % sdkver), - os.path.join(include, '%swinrt' % sdkver)] - - @property - def OSLibpath(self): - """ - Microsoft Windows SDK Libraries Paths - """ - ref = os.path.join(self.si.WindowsSdkDir, 'References') - libpath = [] - - if self.vc_ver <= 9.0: - libpath += self.OSLibraries - - if self.vc_ver >= 11.0: - libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] - - if self.vc_ver >= 14.0: - libpath += [ref, - os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), - os.path.join(ref, r'Windows.Foundation.' - r'UniversalApiContract\1.0.0.0'), - os.path.join(ref, r'Windows.Foundation.' - r'FoundationContract\1.0.0.0'), - os.path.join(ref, r'Windows.Networking.Connectivity.' - r'WwanContract\1.0.0.0'), - os.path.join(self.si.WindowsSdkDir, r'ExtensionSDKs' - r'\Microsoft.VCLibs\%0.1f\References' - r'\CommonConfiguration\neutral' % - self.vc_ver)] - return libpath - - @property - def SdkTools(self): - """ - Microsoft Windows SDK Tools - """ - tools = [os.path.join(self.si.WindowsSdkDir, - 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86')] - - if not self.pi.current_is_x86(): - arch_subdir = self.pi.current_dir(x64=True) - path = 'Bin%s' % arch_subdir - tools += [os.path.join(self.si.WindowsSdkDir, path)] - - if self.vc_ver == 10.0 or self.vc_ver == 11.0: - if self.pi.target_is_x86(): - arch_subdir = '' - else: - arch_subdir = self.pi.current_dir(hidex86=True, x64=True) - path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir - tools += [os.path.join(self.si.WindowsSdkDir, path)] - - if self.si.WindowsSDKExecutablePath: - tools += [self.si.WindowsSDKExecutablePath] - - return tools - - @property - def SdkSetup(self): - """ - Microsoft Windows SDK Setup - """ - if self.vc_ver > 9.0: - return [] - - return [os.path.join(self.si.WindowsSdkDir, 'Setup')] - - @property - def FxTools(self): - """ - Microsoft .NET Framework Tools - """ - pi = self.pi - si = self.si - - if self.vc_ver <= 10.0: - include32 = True - include64 = not pi.target_is_x86() and not pi.current_is_x86() - else: - include32 = pi.target_is_x86() or pi.current_is_x86() - include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' - - tools = [] - if include32: - tools += [os.path.join(si.FrameworkDir32, ver) - for ver in si.FrameworkVersion32] - if include64: - tools += [os.path.join(si.FrameworkDir64, ver) - for ver in si.FrameworkVersion64] - return tools - - @property - def NetFxSDKLibraries(self): - """ - Microsoft .Net Framework SDK Libraries - """ - if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: - return [] - - arch_subdir = self.pi.target_dir(x64=True) - return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] - - @property - def NetFxSDKIncludes(self): - """ - Microsoft .Net Framework SDK Includes - """ - if self.vc_ver < 14.0 or not self.si.NetFxSdkDir: - return [] - - return [os.path.join(self.si.NetFxSdkDir, r'include\um')] - - @property - def VsTDb(self): - """ - Microsoft Visual Studio Team System Database - """ - return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')] - - @property - def MSBuild(self): - """ - Microsoft Build Engine - """ - if self.vc_ver < 12.0: - return [] - - arch_subdir = self.pi.current_dir(hidex86=True) - path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir) - return [os.path.join(self.si.ProgramFilesx86, path)] - - @property - def HTMLHelpWorkshop(self): - """ - Microsoft HTML Help Workshop - """ - if self.vc_ver < 11.0: - return [] - - return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')] - - @property - def UCRTLibraries(self): - """ - Microsoft Universal CRT Libraries - """ - if self.vc_ver < 14.0: - return [] - - arch_subdir = self.pi.target_dir(x64=True) - lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') - ucrtver = self._get_content_dirname(lib) - return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] - - @property - def UCRTIncludes(self): - """ - Microsoft Universal CRT Include - """ - if self.vc_ver < 14.0: - return [] - - include = os.path.join(self.si.UniversalCRTSdkDir, 'include') - ucrtver = self._get_content_dirname(include) - return [os.path.join(include, '%sucrt' % ucrtver)] - - @property - def FSharp(self): - """ - Microsoft Visual F# - """ - if self.vc_ver < 11.0 and self.vc_ver > 12.0: - return [] - - return self.si.FSharpInstallDir - - @property - def VCRuntimeRedist(self): - """ - Microsoft Visual C++ runtime redistribuable dll - """ - arch_subdir = self.pi.target_dir(x64=True) - vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' - vcruntime = vcruntime % (arch_subdir, self.vc_ver, self.vc_ver) - return os.path.join(self.si.VCInstallDir, vcruntime) - - def return_env(self, exists=True): - """ - Return environment dict. - - Parameters - ---------- - exists: bool - It True, only return existing paths. - """ - env = dict( - include=self._build_paths('include', - [self.VCIncludes, - self.OSIncludes, - self.UCRTIncludes, - self.NetFxSDKIncludes], - exists), - lib=self._build_paths('lib', - [self.VCLibraries, - self.OSLibraries, - self.FxTools, - self.UCRTLibraries, - self.NetFxSDKLibraries], - exists), - libpath=self._build_paths('libpath', - [self.VCLibraries, - self.FxTools, - self.VCStoreRefs, - self.OSLibpath], - exists), - path=self._build_paths('path', - [self.VCTools, - self.VSTools, - self.VsTDb, - self.SdkTools, - self.SdkSetup, - self.FxTools, - self.MSBuild, - self.HTMLHelpWorkshop, - self.FSharp], - exists), - ) - if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist): - env['py_vcruntime_redist'] = self.VCRuntimeRedist - return env - - def _build_paths(self, name, spec_path_lists, exists): - """ - Given an environment variable name and specified paths, - return a pathsep-separated string of paths containing - unique, extant, directories from those paths and from - the environment variable. Raise an error if no paths - are resolved. - """ - # flatten spec_path_lists - spec_paths = itertools.chain.from_iterable(spec_path_lists) - env_paths = os.environ.get(name, '').split(os.pathsep) - paths = itertools.chain(spec_paths, env_paths) - extant_paths = list(filter(os.path.isdir, paths)) if exists else paths - if not extant_paths: - msg = "%s environment variable is empty" % name.upper() - raise distutils.errors.DistutilsPlatformError(msg) - unique_paths = self._unique_everseen(extant_paths) - return os.pathsep.join(unique_paths) - - # from Python docs - def _unique_everseen(self, iterable, key=None): - """ - List unique elements, preserving order. - Remember all elements ever seen. - - _unique_everseen('AAAABBBCCDAABBB') --> A B C D - - _unique_everseen('ABBCcAD', str.lower) --> A B C D - """ - seen = set() - seen_add = seen.add - if key is None: - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - else: - for element in iterable: - k = key(element) - if k not in seen: - seen_add(k) - yield element - - def _get_content_dirname(self, path): - """ - Return name of the first dir in path or '' if no dir found. - - Parameters - ---------- - path: str - Path where search dir. - - Return - ------ - foldername: str - "name\" or "" - """ - try: - name = os.listdir(path) - if name: - return '%s\\' % name[0] - return '' - except FileNotFoundError: - return '' diff --git a/setuptools/tests/test_msvc9compiler.py b/setuptools/tests/test_msvc9compiler.py index 09e0460c..86475834 100644 --- a/setuptools/tests/test_msvc9compiler.py +++ b/setuptools/tests/test_msvc9compiler.py @@ -69,7 +69,7 @@ class TestModulePatch: def test_patched(self): "Test the module is actually patched" mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ - assert mod_name == "setuptools.msvc9_support", "find_vcvarsall unpatched" + assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" def test_no_registry_entryies_means_nothing_found(self): """ -- cgit v1.2.1 From 22da89cd92578bc5af6375276a6b3f2c5bd98fb1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:09:58 -0400 Subject: =?UTF-8?q?Bump=20version:=2023.1.0=20=E2=86=92=2023.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index d371955f..609fdd98 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 23.1.0 +current_version = 23.2.0 commit = True tag = True diff --git a/setup.py b/setup.py index 558bd345..764b5f87 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="23.1.0", + version="23.2.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 2370ce3abb2f92e84948aaafa398ce56587d7bb2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:09:58 -0400 Subject: Added tag v23.2.0 for changeset 65b3fe899db4 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index b27122d0..07723c39 100644 --- a/.hgtags +++ b/.hgtags @@ -274,3 +274,4 @@ efee7d74a8478c0d08c801fb520e41b6e04d0dda v22.0.3 d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 8664c631bf3a817a7deba86c13b67eccc1f81091 v23.0.0 6c74559c732c56f61b465d613458ec1a930884b6 v23.1.0 +65b3fe899db4086e66afa067a1311eea2a88d5e2 v23.2.0 -- cgit v1.2.1 From cbc3e03505a6c2a1893312c0ec555b2dec615000 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:16:19 -0400 Subject: Update changelog --- CHANGES.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index a529514d..0ecdf454 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,9 +2,12 @@ CHANGES ======= -v23.2.0 +v23.2.1 ------- +Re-release of v23.2.0, which was missing the intended +commits. + * #623: Remove used of deprecated 'U' flag when reading manifests. -- cgit v1.2.1 From fece3b75874e79f01cecedd41eb035f180400a18 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:21:44 -0400 Subject: =?UTF-8?q?Bump=20version:=2023.2.0=20=E2=86=92=2023.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 609fdd98..9d768989 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 23.2.0 +current_version = 23.2.1 commit = True tag = True diff --git a/setup.py b/setup.py index 764b5f87..6963334a 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="23.2.0", + version="23.2.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 94d97d07df600cca0d51703b13297b73f5b1d9d0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:21:51 -0400 Subject: Added tag v23.2.1 for changeset a011298221c3 --- .hgtags | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgtags b/.hgtags index 07723c39..11d64b6c 100644 --- a/.hgtags +++ b/.hgtags @@ -275,3 +275,5 @@ d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 8664c631bf3a817a7deba86c13b67eccc1f81091 v23.0.0 6c74559c732c56f61b465d613458ec1a930884b6 v23.1.0 65b3fe899db4086e66afa067a1311eea2a88d5e2 v23.2.0 +e10c848a82ffb925741c65dd8a8fc8b50b3c3e14 v23.2.1 +a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 -- cgit v1.2.1 From 349e33139adf073c3b55065b1de658868622ccdd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:27:59 -0400 Subject: Rename test for msvc module as well --- setuptools/tests/test_msvc.py | 179 +++++++++++++++++++++++++++++++++ setuptools/tests/test_msvc9compiler.py | 179 --------------------------------- 2 files changed, 179 insertions(+), 179 deletions(-) create mode 100644 setuptools/tests/test_msvc.py delete mode 100644 setuptools/tests/test_msvc9compiler.py diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py new file mode 100644 index 00000000..86475834 --- /dev/null +++ b/setuptools/tests/test_msvc.py @@ -0,0 +1,179 @@ +""" +Tests for msvc9compiler. +""" + +import os +import contextlib +import distutils.errors + +import pytest +try: + from unittest import mock +except ImportError: + import mock + +from . import contexts + +# importing only setuptools should apply the patch +__import__('setuptools') + +pytest.importorskip("distutils.msvc9compiler") + + +def mock_reg(hkcu=None, hklm=None): + """ + Return a mock for distutils.msvc9compiler.Reg, patched + to mock out the functions that access the registry. + """ + + _winreg = getattr(distutils.msvc9compiler, '_winreg', None) + winreg = getattr(distutils.msvc9compiler, 'winreg', _winreg) + + hives = { + winreg.HKEY_CURRENT_USER: hkcu or {}, + winreg.HKEY_LOCAL_MACHINE: hklm or {}, + } + + @classmethod + def read_keys(cls, base, key): + """Return list of registry keys.""" + hive = hives.get(base, {}) + return [ + k.rpartition('\\')[2] + for k in hive if k.startswith(key.lower()) + ] + + @classmethod + def read_values(cls, base, key): + """Return dict of registry keys and values.""" + hive = hives.get(base, {}) + return dict( + (k.rpartition('\\')[2], hive[k]) + for k in hive if k.startswith(key.lower()) + ) + + return mock.patch.multiple(distutils.msvc9compiler.Reg, + read_keys=read_keys, read_values=read_values) + + +class TestModulePatch: + """ + Ensure that importing setuptools is sufficient to replace + the standard find_vcvarsall function with a version that + recognizes the "Visual C++ for Python" package. + """ + + key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir' + key_64 = r'software\wow6432node\microsoft\devdiv\vcforpython\9.0\installdir' + + def test_patched(self): + "Test the module is actually patched" + mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ + assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" + + def test_no_registry_entryies_means_nothing_found(self): + """ + No registry entries or environment variable should lead to an error + directing the user to download vcpython27. + """ + find_vcvarsall = distutils.msvc9compiler.find_vcvarsall + query_vcvarsall = distutils.msvc9compiler.query_vcvarsall + + with contexts.environment(VS90COMNTOOLS=None): + with mock_reg(): + assert find_vcvarsall(9.0) is None + + expected = distutils.errors.DistutilsPlatformError + with pytest.raises(expected) as exc: + query_vcvarsall(9.0) + assert 'aka.ms/vcpython27' in str(exc) + + @pytest.yield_fixture + def user_preferred_setting(self): + """ + Set up environment with different install dirs for user vs. system + and yield the user_install_dir for the expected result. + """ + with self.mock_install_dir() as user_install_dir: + with self.mock_install_dir() as system_install_dir: + reg = mock_reg( + hkcu={ + self.key_32: user_install_dir, + }, + hklm={ + self.key_32: system_install_dir, + self.key_64: system_install_dir, + }, + ) + with reg: + yield user_install_dir + + def test_prefer_current_user(self, user_preferred_setting): + """ + Ensure user's settings are preferred. + """ + result = distutils.msvc9compiler.find_vcvarsall(9.0) + expected = os.path.join(user_preferred_setting, 'vcvarsall.bat') + assert expected == result + + @pytest.yield_fixture + def local_machine_setting(self): + """ + Set up environment with only the system environment configured. + """ + with self.mock_install_dir() as system_install_dir: + reg = mock_reg( + hklm={ + self.key_32: system_install_dir, + }, + ) + with reg: + yield system_install_dir + + def test_local_machine_recognized(self, local_machine_setting): + """ + Ensure machine setting is honored if user settings are not present. + """ + result = distutils.msvc9compiler.find_vcvarsall(9.0) + expected = os.path.join(local_machine_setting, 'vcvarsall.bat') + assert expected == result + + @pytest.yield_fixture + def x64_preferred_setting(self): + """ + Set up environment with 64-bit and 32-bit system settings configured + and yield the canonical location. + """ + with self.mock_install_dir() as x32_dir: + with self.mock_install_dir() as x64_dir: + reg = mock_reg( + hklm={ + # This *should* only exist on 32-bit machines + self.key_32: x32_dir, + # This *should* only exist on 64-bit machines + self.key_64: x64_dir, + }, + ) + with reg: + yield x32_dir + + def test_ensure_64_bit_preferred(self, x64_preferred_setting): + """ + Ensure 64-bit system key is preferred. + """ + result = distutils.msvc9compiler.find_vcvarsall(9.0) + expected = os.path.join(x64_preferred_setting, 'vcvarsall.bat') + assert expected == result + + @staticmethod + @contextlib.contextmanager + def mock_install_dir(): + """ + Make a mock install dir in a unique location so that tests can + distinguish which dir was detected in a given scenario. + """ + with contexts.tempdir() as result: + vcvarsall = os.path.join(result, 'vcvarsall.bat') + with open(vcvarsall, 'w'): + pass + yield result diff --git a/setuptools/tests/test_msvc9compiler.py b/setuptools/tests/test_msvc9compiler.py deleted file mode 100644 index 86475834..00000000 --- a/setuptools/tests/test_msvc9compiler.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -Tests for msvc9compiler. -""" - -import os -import contextlib -import distutils.errors - -import pytest -try: - from unittest import mock -except ImportError: - import mock - -from . import contexts - -# importing only setuptools should apply the patch -__import__('setuptools') - -pytest.importorskip("distutils.msvc9compiler") - - -def mock_reg(hkcu=None, hklm=None): - """ - Return a mock for distutils.msvc9compiler.Reg, patched - to mock out the functions that access the registry. - """ - - _winreg = getattr(distutils.msvc9compiler, '_winreg', None) - winreg = getattr(distutils.msvc9compiler, 'winreg', _winreg) - - hives = { - winreg.HKEY_CURRENT_USER: hkcu or {}, - winreg.HKEY_LOCAL_MACHINE: hklm or {}, - } - - @classmethod - def read_keys(cls, base, key): - """Return list of registry keys.""" - hive = hives.get(base, {}) - return [ - k.rpartition('\\')[2] - for k in hive if k.startswith(key.lower()) - ] - - @classmethod - def read_values(cls, base, key): - """Return dict of registry keys and values.""" - hive = hives.get(base, {}) - return dict( - (k.rpartition('\\')[2], hive[k]) - for k in hive if k.startswith(key.lower()) - ) - - return mock.patch.multiple(distutils.msvc9compiler.Reg, - read_keys=read_keys, read_values=read_values) - - -class TestModulePatch: - """ - Ensure that importing setuptools is sufficient to replace - the standard find_vcvarsall function with a version that - recognizes the "Visual C++ for Python" package. - """ - - key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir' - key_64 = r'software\wow6432node\microsoft\devdiv\vcforpython\9.0\installdir' - - def test_patched(self): - "Test the module is actually patched" - mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ - assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" - - def test_no_registry_entryies_means_nothing_found(self): - """ - No registry entries or environment variable should lead to an error - directing the user to download vcpython27. - """ - find_vcvarsall = distutils.msvc9compiler.find_vcvarsall - query_vcvarsall = distutils.msvc9compiler.query_vcvarsall - - with contexts.environment(VS90COMNTOOLS=None): - with mock_reg(): - assert find_vcvarsall(9.0) is None - - expected = distutils.errors.DistutilsPlatformError - with pytest.raises(expected) as exc: - query_vcvarsall(9.0) - assert 'aka.ms/vcpython27' in str(exc) - - @pytest.yield_fixture - def user_preferred_setting(self): - """ - Set up environment with different install dirs for user vs. system - and yield the user_install_dir for the expected result. - """ - with self.mock_install_dir() as user_install_dir: - with self.mock_install_dir() as system_install_dir: - reg = mock_reg( - hkcu={ - self.key_32: user_install_dir, - }, - hklm={ - self.key_32: system_install_dir, - self.key_64: system_install_dir, - }, - ) - with reg: - yield user_install_dir - - def test_prefer_current_user(self, user_preferred_setting): - """ - Ensure user's settings are preferred. - """ - result = distutils.msvc9compiler.find_vcvarsall(9.0) - expected = os.path.join(user_preferred_setting, 'vcvarsall.bat') - assert expected == result - - @pytest.yield_fixture - def local_machine_setting(self): - """ - Set up environment with only the system environment configured. - """ - with self.mock_install_dir() as system_install_dir: - reg = mock_reg( - hklm={ - self.key_32: system_install_dir, - }, - ) - with reg: - yield system_install_dir - - def test_local_machine_recognized(self, local_machine_setting): - """ - Ensure machine setting is honored if user settings are not present. - """ - result = distutils.msvc9compiler.find_vcvarsall(9.0) - expected = os.path.join(local_machine_setting, 'vcvarsall.bat') - assert expected == result - - @pytest.yield_fixture - def x64_preferred_setting(self): - """ - Set up environment with 64-bit and 32-bit system settings configured - and yield the canonical location. - """ - with self.mock_install_dir() as x32_dir: - with self.mock_install_dir() as x64_dir: - reg = mock_reg( - hklm={ - # This *should* only exist on 32-bit machines - self.key_32: x32_dir, - # This *should* only exist on 64-bit machines - self.key_64: x64_dir, - }, - ) - with reg: - yield x32_dir - - def test_ensure_64_bit_preferred(self, x64_preferred_setting): - """ - Ensure 64-bit system key is preferred. - """ - result = distutils.msvc9compiler.find_vcvarsall(9.0) - expected = os.path.join(x64_preferred_setting, 'vcvarsall.bat') - assert expected == result - - @staticmethod - @contextlib.contextmanager - def mock_install_dir(): - """ - Make a mock install dir in a unique location so that tests can - distinguish which dir was detected in a given scenario. - """ - with contexts.tempdir() as result: - vcvarsall = os.path.join(result, 'vcvarsall.bat') - with open(vcvarsall, 'w'): - pass - yield result -- cgit v1.2.1 From f2a0b309367cfdade3c0c18d8c69612010351120 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:38:21 -0400 Subject: Extract template as variable to avoid line continuation. --- setuptools/msvc.py | 4 ++-- setuptools/tests/test_msvc.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 63031ac2..500a00f9 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -219,8 +219,8 @@ def _augment_exception(exc, version, arch=''): if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed - message = 'Microsoft Visual C++ %0.1f is required (%s).' %\ - (version, message) + tmpl = 'Microsoft Visual C++ %0.1f is required (%s).' + message = tmpl % version, message msdownload = r'www.microsoft.com/download/details.aspx?id=%d' if version == 9.0: if arch.lower().find('ia64') > -1: diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index 86475834..2fbd56bf 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -1,5 +1,5 @@ """ -Tests for msvc9compiler. +Tests for msvc support module. """ import os -- cgit v1.2.1 From 0746957d513d74e6a9f5e2375a10d1d4a0a5108c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:41:20 -0400 Subject: Remove superfluous raw strings --- setuptools/msvc.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 500a00f9..27118ed9 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -219,9 +219,9 @@ def _augment_exception(exc, version, arch=''): if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ %0.1f is required (%s).' - message = tmpl % version, message - msdownload = r'www.microsoft.com/download/details.aspx?id=%d' + tmpl = 'Microsoft Visual C++ {version:0.1f} is required {message}.' + message = tmpl.format(**locals()) + msdownload = 'www.microsoft.com/download/details.aspx?id=%d' if version == 9.0: if arch.lower().find('ia64') > -1: # For VC++ 9.0, if IA64 support is needed, redirect user @@ -232,7 +232,7 @@ def _augment_exception(exc, version, arch=''): # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : # This redirection link is maintained by Microsoft. # Contact vspython@microsoft.com if it needs updating. - message += r' Get it from http://aka.ms/vcpython27' + message += ' Get it from http://aka.ms/vcpython27' elif version == 10.0: # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK 7.1": ' @@ -365,7 +365,7 @@ class RegistryInfo: """ Microsoft Visual Studio root registry key. """ - return os.path.join(self.microsoft, r'VisualStudio') + return os.path.join(self.microsoft, 'VisualStudio') @property def sxs(self): @@ -860,7 +860,7 @@ class EnvironmentInfo: arch_subdir = self.pi.target_dir(x64=True) lib = os.path.join(self.si.WindowsSdkDir, 'lib') libver = self._get_content_dirname(lib) - return [os.path.join(lib, r'%sum%s' % (libver, arch_subdir))] + return [os.path.join(lib, '%sum%s' % (libver, arch_subdir))] @property def OSIncludes(self): @@ -898,13 +898,13 @@ class EnvironmentInfo: if self.vc_ver >= 14.0: libpath += [ref, os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), - os.path.join(ref, r'Windows.Foundation.' + os.path.join(ref, 'Windows.Foundation.' r'UniversalApiContract\1.0.0.0'), - os.path.join(ref, r'Windows.Foundation.' + os.path.join(ref, 'Windows.Foundation.' r'FoundationContract\1.0.0.0'), - os.path.join(ref, r'Windows.Networking.Connectivity.' + os.path.join(ref, 'Windows.Networking.Connectivity.' r'WwanContract\1.0.0.0'), - os.path.join(self.si.WindowsSdkDir, r'ExtensionSDKs' + os.path.join(self.si.WindowsSdkDir, 'ExtensionSDKs' r'\Microsoft.VCLibs\%0.1f\References' r'\CommonConfiguration\neutral' % self.vc_ver)] -- cgit v1.2.1 From e404a4aa3b3aa62c5f2c956f3076ddb9155a6829 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:47:53 -0400 Subject: Reindent to avoid raw strings and hanging indents. Let os.path.join provide the backslash characters. --- setuptools/msvc.py | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 27118ed9..0b483ea3 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -896,18 +896,34 @@ class EnvironmentInfo: libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')] if self.vc_ver >= 14.0: - libpath += [ref, - os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), - os.path.join(ref, 'Windows.Foundation.' - r'UniversalApiContract\1.0.0.0'), - os.path.join(ref, 'Windows.Foundation.' - r'FoundationContract\1.0.0.0'), - os.path.join(ref, 'Windows.Networking.Connectivity.' - r'WwanContract\1.0.0.0'), - os.path.join(self.si.WindowsSdkDir, 'ExtensionSDKs' - r'\Microsoft.VCLibs\%0.1f\References' - r'\CommonConfiguration\neutral' % - self.vc_ver)] + libpath += [ + ref, + os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), + os.path.join( + ref, + 'Windows.Foundation.UniversalApiContract' + '1.0.0.0', + ), + os.path.join( + ref, + 'Windows.Foundation.FoundationContract', + '1.0.0.0', + ), + os.path.join( + ref, + 'Windows.Networking.Connectivity.WwanContract' + '1.0.0.0', + ), + os.path.join( + self.si.WindowsSdkDir, + 'ExtensionSDKs', + 'Microsoft.VCLibs', + '%0.1f' % self.vc_ver, + 'References', + 'CommonConfiguration', + 'neutral', + ), + ] return libpath @property -- cgit v1.2.1 From 4742d661ab7b6ca643610b2b1f5100cd582d75b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 12:48:31 -0400 Subject: Extract variable for bin_dir --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 0b483ea3..bbf66570 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -931,8 +931,8 @@ class EnvironmentInfo: """ Microsoft Windows SDK Tools """ - tools = [os.path.join(self.si.WindowsSdkDir, - 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86')] + bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' + tools = [os.path.join(self.si.WindowsSdkDir, bin_dir)] if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) -- cgit v1.2.1 From 590ae3ae964c201fb58273e074b10c4d432592bb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 13:31:12 -0400 Subject: Update changelog --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cf81d587..d94da2dc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,8 +2,8 @@ CHANGES ======= -Next ----- +v24.0.0 +------- * Pull Request #174: Add more aggressive support for Windows SDK in msvc9compiler patch. -- cgit v1.2.1 From f26b15fb8296d0d008b9cf857cf33eb049a95ff2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 13:32:36 -0400 Subject: =?UTF-8?q?Bump=20version:=2023.2.1=20=E2=86=92=2024.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 9d768989..e35e1b8f 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 23.2.1 +current_version = 24.0.0 commit = True tag = True diff --git a/setup.py b/setup.py index 6963334a..56029892 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="23.2.1", + version="24.0.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From ca2c0f9aae22081a458848e9ca6baf71f54b88e2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Jul 2016 13:32:36 -0400 Subject: Added tag v24.0.0 for changeset 8d37b17a93ec --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 11d64b6c..ccf6efff 100644 --- a/.hgtags +++ b/.hgtags @@ -277,3 +277,4 @@ d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 65b3fe899db4086e66afa067a1311eea2a88d5e2 v23.2.0 e10c848a82ffb925741c65dd8a8fc8b50b3c3e14 v23.2.1 a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 +8d37b17a93ec3e5fff9e040fc3f14ab7b7b24b2c v24.0.0 -- cgit v1.2.1 From 79d1fc0d9f50af9f51a3c7a7188e4afc644a4aac Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 14:04:06 +0200 Subject: msvc fixe Fixes : - Bad argument name - Better Python 2 compatibility --- setuptools/msvc.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index bbf66570..a03f4fe3 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -203,7 +203,7 @@ def msvc14_get_vc_env(plat_spec): # If error, try to set environment directly try: - return EnvironmentInfo(plat_spec, vc_ver_min=14.0).return_env() + return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, 14.0) raise @@ -442,11 +442,11 @@ class RegistryInfo: for hkey in self.HKEYS: try: bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) - except FileNotFoundError: + except IOError: continue try: return winreg.QueryValueEx(bkey, name)[0] - except FileNotFoundError: + except IOError: pass @@ -489,7 +489,7 @@ class SystemInfo: for key in vckeys: try: bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) - except FileNotFoundError: + except IOError: continue subkeys, values, _ = winreg.QueryInfoKey(bkey) for i in range(values): @@ -1187,5 +1187,5 @@ class EnvironmentInfo: if name: return '%s\\' % name[0] return '' - except FileNotFoundError: + except IOError: return '' -- cgit v1.2.1 From b90df6ddf053d18c32c72502521eecb36bc454a6 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 14:06:54 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index a03f4fe3..d5a48dce 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -237,6 +237,10 @@ def _augment_exception(exc, version, arch=''): # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK 7.1": ' message += msdownload % 8279 + elif version >= 14.0: + # For VC++ 14.0 Redirect user to Visual C++ Build Tools + message += ' Get it with "Visual C++ Build Tools": ' + r'http://landinghub.visualstudio.com/visual-cpp-build-tools' exc.args = (message, ) -- cgit v1.2.1 From 4751aeef452878881a2b0d81a2624c1c5e72b911 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 14:33:36 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index d5a48dce..b85d66e0 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -3,7 +3,6 @@ This module adds improved support for Microsoft Visual C++ compilers. """ import os -import collections import itertools import distutils.errors from setuptools.extern.six.moves import filterfalse @@ -21,7 +20,7 @@ except ImportError: HKEY_CURRENT_USER = None HKEY_LOCAL_MACHINE = None HKEY_CLASSES_ROOT = None - safe_env = collections.defaultdict(lambda: '') + safe_env = dict() try: @@ -36,7 +35,7 @@ try: except ImportError: pass - + unpatched = dict() @@ -237,10 +236,6 @@ def _augment_exception(exc, version, arch=''): # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK 7.1": ' message += msdownload % 8279 - elif version >= 14.0: - # For VC++ 14.0 Redirect user to Visual C++ Build Tools - message += ' Get it with "Visual C++ Build Tools": ' - r'http://landinghub.visualstudio.com/visual-cpp-build-tools' exc.args = (message, ) @@ -254,7 +249,7 @@ class PlatformInfo: arch: str Target architecture. """ - current_cpu = safe_env['processor_architecture'].lower() + current_cpu = safe_env.get('processor_architecture', '').lower() def __init__(self, arch): self.arch = arch.lower().replace('x64', 'amd64') @@ -467,9 +462,9 @@ class SystemInfo: """ # Variables and properties in this class use originals CamelCase variables # names from Microsoft source files for more easy comparaison. - WinDir = safe_env['WinDir'] - ProgramFiles = safe_env['ProgramFiles'] - ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) + WinDir = safe_env.get('WinDir', '') + ProgramFiles = safe_env.get('ProgramFiles', '') + ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles) def __init__(self, registry_info, vc_ver=None): self.ri = registry_info -- cgit v1.2.1 From 2d0a77530153a53e3a9a00ae1f7d0d800d6af604 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:06:11 +0200 Subject: Update CHANGES.rst --- CHANGES.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index d94da2dc..2f2d689f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,7 +6,11 @@ v24.0.0 ------- * Pull Request #174: Add more aggressive support for - Windows SDK in msvc9compiler patch. + standalone Microsoft Visual C++ compilers in + msvc9compiler patch. + Particularly : Windows SDK 6.0 and 6.1 + (MSVC++ 9.0), Windows SDK 7.0 (MSVC++ 10.0), + Visual C++ Build Tools 2015 (MSVC++14) * Renamed ``setuptools.msvc9_support`` to ``setuptools.msvc``. -- cgit v1.2.1 From 7b9290771ec850cf7f5e21e168048b12e1e59cf4 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:17:08 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index b85d66e0..8216e94a 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -1,27 +1,16 @@ """ This module adds improved support for Microsoft Visual C++ compilers. """ - import os +import platform import itertools import distutils.errors from setuptools.extern.six.moves import filterfalse try: from setuptools.extern.six.moves import winreg - safe_env = os.environ except ImportError: - """ - Mock winreg and environ so the module can be imported - on this platform. - """ - class winreg: - HKEY_USERS = None - HKEY_CURRENT_USER = None - HKEY_LOCAL_MACHINE = None - HKEY_CLASSES_ROOT = None - safe_env = dict() - + pass try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) @@ -35,7 +24,7 @@ try: except ImportError: pass - + unpatched = dict() @@ -57,6 +46,10 @@ def patch_for_specialized_compiler(): Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ + if platform.system() != Windows: + # Compilers only availables on Microsoft Windows + return + if 'distutils' not in globals(): # The module isn't available to be patched return @@ -249,7 +242,7 @@ class PlatformInfo: arch: str Target architecture. """ - current_cpu = safe_env.get('processor_architecture', '').lower() + current_cpu = os.environ.get('processor_architecture', '').lower() def __init__(self, arch): self.arch = arch.lower().replace('x64', 'amd64') @@ -462,9 +455,9 @@ class SystemInfo: """ # Variables and properties in this class use originals CamelCase variables # names from Microsoft source files for more easy comparaison. - WinDir = safe_env.get('WinDir', '') - ProgramFiles = safe_env.get('ProgramFiles', '') - ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles) + WinDir = os.environ.get('WinDir', '') + ProgramFiles = os.environ.get('ProgramFiles', '') + ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) def __init__(self, registry_info, vc_ver=None): self.ri = registry_info -- cgit v1.2.1 From b4b913b18c07a9d2bce8ae57da7e8c45bf17c28b Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:22:40 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 8216e94a..5e6c1c25 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -9,8 +9,18 @@ from setuptools.extern.six.moves import filterfalse try: from setuptools.extern.six.moves import winreg + safe_env = os.environ except ImportError: - pass + """ + Mock winreg and environ so the module can be imported + on this platform. + """ + class winreg: + HKEY_USERS = None + HKEY_CURRENT_USER = None + HKEY_LOCAL_MACHINE = None + HKEY_CLASSES_ROOT = None + safe_env = collections.defaultdict(lambda: '') try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) @@ -242,7 +252,7 @@ class PlatformInfo: arch: str Target architecture. """ - current_cpu = os.environ.get('processor_architecture', '').lower() + current_cpu = safe_env.get('processor_architecture', '').lower() def __init__(self, arch): self.arch = arch.lower().replace('x64', 'amd64') @@ -455,9 +465,9 @@ class SystemInfo: """ # Variables and properties in this class use originals CamelCase variables # names from Microsoft source files for more easy comparaison. - WinDir = os.environ.get('WinDir', '') - ProgramFiles = os.environ.get('ProgramFiles', '') - ProgramFilesx86 = os.environ.get('ProgramFiles(x86)', ProgramFiles) + WinDir = safe_env.get('WinDir', '') + ProgramFiles = safe_env.get('ProgramFiles', '') + ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles) def __init__(self, registry_info, vc_ver=None): self.ri = registry_info @@ -1128,7 +1138,7 @@ class EnvironmentInfo: """ # flatten spec_path_lists spec_paths = itertools.chain.from_iterable(spec_path_lists) - env_paths = os.environ.get(name, '').split(os.pathsep) + env_paths = safe_env.get(name, '').split(os.pathsep) paths = itertools.chain(spec_paths, env_paths) extant_paths = list(filter(os.path.isdir, paths)) if exists else paths if not extant_paths: -- cgit v1.2.1 From 2aa04076e57845abdf9e0cc5238bdcc23ae5e58e Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:26:20 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 5e6c1c25..0851b193 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -7,10 +7,10 @@ import itertools import distutils.errors from setuptools.extern.six.moves import filterfalse -try: +if platform.system() == Windows: from setuptools.extern.six.moves import winreg safe_env = os.environ -except ImportError: +else: """ Mock winreg and environ so the module can be imported on this platform. -- cgit v1.2.1 From a0bc231f8fee1f2df0c04b92eb070e66536d622e Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:28:12 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 0851b193..f1213f32 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -4,6 +4,7 @@ This module adds improved support for Microsoft Visual C++ compilers. import os import platform import itertools +import collections import distutils.errors from setuptools.extern.six.moves import filterfalse -- cgit v1.2.1 From c12e784cce4104244353e18e73dedbb588061de7 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:31:18 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index f1213f32..990382c0 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -8,7 +8,7 @@ import collections import distutils.errors from setuptools.extern.six.moves import filterfalse -if platform.system() == Windows: +if platform.system() == 'Windows': from setuptools.extern.six.moves import winreg safe_env = os.environ else: @@ -57,7 +57,7 @@ def patch_for_specialized_compiler(): Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ - if platform.system() != Windows: + if platform.system() != 'Windows': # Compilers only availables on Microsoft Windows return -- cgit v1.2.1 From fb2673566eb04bcf315a5dc28193a59c1dce902b Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 15:42:45 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 990382c0..8c612129 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -4,7 +4,6 @@ This module adds improved support for Microsoft Visual C++ compilers. import os import platform import itertools -import collections import distutils.errors from setuptools.extern.six.moves import filterfalse @@ -21,7 +20,7 @@ else: HKEY_CURRENT_USER = None HKEY_LOCAL_MACHINE = None HKEY_CLASSES_ROOT = None - safe_env = collections.defaultdict(lambda: '') + safe_env = dict() try: # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) -- cgit v1.2.1 From a2ce15c43be744cbf0cbf8763950f5f5d566625f Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 16:00:49 +0200 Subject: Update CHANGES.rst --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2f2d689f..a3599f27 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v24.0.1 +------- + +* Fixes on ``setuptools.msvc`` mainly for Python 2 + and Linux. + v24.0.0 ------- -- cgit v1.2.1 From 8932c2d04e6c4258093cf919e525c61c7663250e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 Jul 2016 12:19:18 -0400 Subject: Update changelog to reference issues --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a3599f27..5f2b6076 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,8 +5,8 @@ CHANGES v24.0.1 ------- -* Fixes on ``setuptools.msvc`` mainly for Python 2 - and Linux. +* #625 and #626: Fixes on ``setuptools.msvc`` mainly + for Python 2 and Linux. v24.0.0 ------- -- cgit v1.2.1 From 6e405e85df9f7694f549bb98f4c6a080e86d79e7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 Jul 2016 12:19:26 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.0.0=20=E2=86=92=2024.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index e35e1b8f..d7eff8d1 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.0.0 +current_version = 24.0.1 commit = True tag = True diff --git a/setup.py b/setup.py index 56029892..fde901a5 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.0.0", + version="24.0.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 6be486070f5badd8e8d954977cc673714256568f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 Jul 2016 12:19:26 -0400 Subject: Added tag v24.0.1 for changeset 130a58f9503f --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index ccf6efff..f430934b 100644 --- a/.hgtags +++ b/.hgtags @@ -278,3 +278,4 @@ d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 e10c848a82ffb925741c65dd8a8fc8b50b3c3e14 v23.2.1 a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 8d37b17a93ec3e5fff9e040fc3f14ab7b7b24b2c v24.0.0 +130a58f9503fe07ca8c7a34675b7d3a976f163d7 v24.0.1 -- cgit v1.2.1 From 7b303ef300faad048b21005e17d1ca83e4ad3a18 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 19:04:03 +0200 Subject: Minor change : Link to MSVC14 Standalone Add the new link to "Microsoft Visual C++ Build Tools" if MSVC14 is not installed. --- setuptools/msvc.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 8c612129..b543c568 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -221,7 +221,7 @@ def _augment_exception(exc, version, arch=''): if "vcvarsall" in message.lower() or "visual c" in message.lower(): # Special error message if MSVC++ not installed - tmpl = 'Microsoft Visual C++ {version:0.1f} is required {message}.' + tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' message = tmpl.format(**locals()) msdownload = 'www.microsoft.com/download/details.aspx?id=%d' if version == 9.0: @@ -239,6 +239,10 @@ def _augment_exception(exc, version, arch=''): # For VC++ 10.0 Redirect user to Windows SDK 7.1 message += ' Get it with "Microsoft Windows SDK 7.1": ' message += msdownload % 8279 + elif version >= 14.0: + # For VC++ 14.0 Redirect user to Visual C++ Build Tools + message += (' Get it with "Microsoft Visual C++ Build Tools": ' + r'http://landinghub.visualstudio.com/visual-cpp-build-tools') exc.args = (message, ) -- cgit v1.2.1 From 46aa62fb47f569ce1e3f5eb9ae83f9b363f9da81 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 19:13:00 +0200 Subject: Update CHANGES.rst --- CHANGES.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 94d99fda..23e85a7b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v24.0.2 +------- + +* If MSVC14 is needed ``setuptools.msvc`` no redirect + user to Visual C++ Build Tools web page. + v24.0.1 ------- @@ -14,8 +20,8 @@ v24.0.0 * Pull Request #174: Add more aggressive support for standalone Microsoft Visual C++ compilers in msvc9compiler patch. - Particularly : Windows SDK 6.0 and 6.1 - (MSVC++ 9.0), Windows SDK 7.0 (MSVC++ 10.0), + Particularly : Windows SDK 6.1 and 7.0 + (MSVC++ 9.0), Windows SDK 7.1 (MSVC++ 10.0), Visual C++ Build Tools 2015 (MSVC++14) * Renamed ``setuptools.msvc9_support`` to ``setuptools.msvc``. -- cgit v1.2.1 From 2a835337bfe7d4831b59c448e729e6a609c2c38f Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 19:13:31 +0200 Subject: Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 23e85a7b..2b01135d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ CHANGES v24.0.2 ------- -* If MSVC14 is needed ``setuptools.msvc`` no redirect +* If MSVC++14 is needed ``setuptools.msvc`` no redirect user to Visual C++ Build Tools web page. v24.0.1 -- cgit v1.2.1 From cb29ee77631530b6e15bc84ba8762bcda0ad4467 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sun, 3 Jul 2016 21:28:19 +0200 Subject: Update CHANGES.rst --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2b01135d..e04bdf05 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,7 @@ CHANGES v24.0.2 ------- -* If MSVC++14 is needed ``setuptools.msvc`` no redirect +* If MSVC++14 is needed ``setuptools.msvc`` now redirect user to Visual C++ Build Tools web page. v24.0.1 -- cgit v1.2.1 From 7ef0e255cd8ed3f99058e8c3c11fd8b537d5fcd0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 4 Jul 2016 11:22:36 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.0.1=20=E2=86=92=2024.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index d7eff8d1..303569f6 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.0.1 +current_version = 24.0.2 commit = True tag = True diff --git a/setup.py b/setup.py index fde901a5..798d4db4 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.0.1", + version="24.0.2", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From e3e21fd3d608d72ebe1ba2e4d2b3a59fbca76554 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 4 Jul 2016 11:22:36 -0400 Subject: Added tag v24.0.2 for changeset 7996c56bf6a2 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f430934b..afb158b1 100644 --- a/.hgtags +++ b/.hgtags @@ -279,3 +279,4 @@ e10c848a82ffb925741c65dd8a8fc8b50b3c3e14 v23.2.1 a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 8d37b17a93ec3e5fff9e040fc3f14ab7b7b24b2c v24.0.0 130a58f9503fe07ca8c7a34675b7d3a976f163d7 v24.0.1 +7996c56bf6a2f81427b2f91eb11e64d690353493 v24.0.2 -- cgit v1.2.1 From 843988ea2e4e4df34abf343e05fa1bac17680f5c Mon Sep 17 00:00:00 2001 From: stepshal Date: Tue, 12 Jul 2016 22:28:51 +0700 Subject: Remove trailing whitespace --- setuptools/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/utils.py b/setuptools/utils.py index 91e4b87f..080b9a8e 100644 --- a/setuptools/utils.py +++ b/setuptools/utils.py @@ -3,9 +3,9 @@ import os.path def cs_path_exists(fspath): - if not os.path.exists(fspath): + if not os.path.exists(fspath): return False # make absolute so we always have a directory abspath = os.path.abspath(fspath) directory, filename = os.path.split(abspath) - return filename in os.listdir(directory) \ No newline at end of file + return filename in os.listdir(directory) -- cgit v1.2.1 From 6d11e88f938f09ef16db4c6064b6e74acba4db1d Mon Sep 17 00:00:00 2001 From: stepshal Date: Tue, 12 Jul 2016 22:00:43 +0700 Subject: Fix quantity of blank lines after code object. --- bootstrap.py | 1 + pavement.py | 2 ++ setup.py | 1 + setuptools/__init__.py | 4 ++++ setuptools/archive_util.py | 2 ++ setuptools/command/bdist_wininst.py | 1 + setuptools/command/build_ext.py | 2 ++ setuptools/command/build_py.py | 1 + setuptools/command/develop.py | 1 + setuptools/command/easy_install.py | 1 + setuptools/command/install_lib.py | 1 + setuptools/command/sdist.py | 1 + setuptools/command/test.py | 2 ++ setuptools/depends.py | 4 +--- setuptools/dist.py | 13 ++++++++++--- setuptools/extension.py | 2 ++ setuptools/lib2to3_ex.py | 4 ++++ setuptools/msvc.py | 1 + setuptools/package_index.py | 17 +++++++++++++++++ setuptools/py26compat.py | 1 + setuptools/py27compat.py | 1 + setuptools/py31compat.py | 3 +++ setuptools/sandbox.py | 34 +++++++++------------------------- setuptools/ssl_support.py | 4 ++++ setuptools/tests/__init__.py | 2 ++ setuptools/tests/py26compat.py | 1 + setuptools/tests/server.py | 5 +++++ setuptools/tests/test_bdist_egg.py | 1 + setuptools/tests/test_build_ext.py | 2 ++ setuptools/tests/test_develop.py | 2 ++ setuptools/tests/test_easy_install.py | 6 ++++++ setuptools/tests/test_egg_info.py | 1 + setuptools/tests/test_find_packages.py | 4 ++++ setuptools/tests/test_packageindex.py | 1 + setuptools/tests/test_sandbox.py | 1 + setuptools/tests/test_sdist.py | 1 - tests/manual_test.py | 3 +++ 37 files changed, 102 insertions(+), 32 deletions(-) diff --git a/bootstrap.py b/bootstrap.py index 70f96258..bf7fb431 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -27,6 +27,7 @@ minimal_egg_info = textwrap.dedent(""" requires.txt = setuptools.command.egg_info:write_requirements """) + def ensure_egg_info(): if os.path.exists('setuptools.egg-info'): return diff --git a/pavement.py b/pavement.py index 303e9bac..f620c790 100644 --- a/pavement.py +++ b/pavement.py @@ -3,10 +3,12 @@ import re from paver.easy import task, path as Path import pip + def remove_all(paths): for path in paths: path.rmtree() if path.isdir() else path.remove() + @task def update_vendored(): vendor = Path('pkg_resources/_vendor') diff --git a/setup.py b/setup.py index 798d4db4..bac726bf 100755 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ import setuptools scripts = [] + def _gen_console_scripts(): yield "easy_install = setuptools.command.easy_install:main" diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 67b57e4f..2523ccc7 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -32,6 +32,7 @@ lib2to3_fixer_packages = ['lib2to3.fixes'] class PackageFinder(object): + @classmethod def find(cls, where='.', exclude=(), include=('*',)): """Return a list all Python packages found within directory 'where' @@ -108,7 +109,9 @@ class PackageFinder(object): """ return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) + class PEP420PackageFinder(PackageFinder): + @staticmethod def _looks_like_package(path): return True @@ -119,6 +122,7 @@ setup = distutils.core.setup _Command = _get_unpatched(_Command) + class Command(_Command): __doc__ = _Command.__doc__ diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index b3c9fa56..4da24ec2 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -15,9 +15,11 @@ import contextlib from pkg_resources import ensure_directory, ContextualZipFile from distutils.errors import DistutilsError + class UnrecognizedFormat(DistutilsError): """Couldn't recognize the archive type""" + def default_filter(src,dst): """The default progress/filter callback; returns True for all files""" return dst diff --git a/setuptools/command/bdist_wininst.py b/setuptools/command/bdist_wininst.py index 073de97b..8243c917 100755 --- a/setuptools/command/bdist_wininst.py +++ b/setuptools/command/bdist_wininst.py @@ -2,6 +2,7 @@ import distutils.command.bdist_wininst as orig class bdist_wininst(orig.bdist_wininst): + def reinitialize_command(self, command, reinit_subcommands=0): """ Supplement reinitialize_command to work around diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 1caf8c81..e6db0764 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -59,7 +59,9 @@ elif os.name != 'nt': if_dl = lambda s: s if have_rtld else '' + class build_ext(_build_ext): + def run(self): """Build extensions in build directory, then copy if --inplace""" old_inplace, self.inplace = self.inplace, 0 diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 0bad8295..b5de9bda 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -15,6 +15,7 @@ try: from setuptools.lib2to3_ex import Mixin2to3 except ImportError: class Mixin2to3: + def run_2to3(self, files, doctests=True): "do nothing" diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 11b5df10..3eb86120 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -186,6 +186,7 @@ class VersionlessRequirement(object): >>> str(adapted_dist.as_requirement()) 'foo' """ + def __init__(self, dist): self.__dist = dist diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 9ca1554e..73bdb0cb 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2158,6 +2158,7 @@ class WindowsScriptWriter(ScriptWriter): class WindowsExecutableLauncherWriter(WindowsScriptWriter): + @classmethod def _get_script_args(cls, type_, name, header, script_text): """ diff --git a/setuptools/command/install_lib.py b/setuptools/command/install_lib.py index 78fe6891..2b31c3e3 100644 --- a/setuptools/command/install_lib.py +++ b/setuptools/command/install_lib.py @@ -3,6 +3,7 @@ import imp from itertools import product, starmap import distutils.command.install_lib as orig + class install_lib(orig.install_lib): """Don't add compiled flags to filenames of non-Python files""" diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index f200b946..041ee42e 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -15,6 +15,7 @@ READMES = 'README', 'README.rst', 'README.txt' _default_revctrl = list + def walk_revctrl(dirname=''): """Find all files under revision control""" for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 39746a02..2d1adba8 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -14,6 +14,7 @@ from setuptools.py31compat import unittest_main class ScanningLoader(TestLoader): + def loadTestsFromModule(self, module, pattern=None): """Return a suite of all tests cases contained in the given module @@ -46,6 +47,7 @@ class ScanningLoader(TestLoader): # adapted from jaraco.classes.properties:NonDataProperty class NonDataProperty(object): + def __init__(self, fget): self.fget = fget diff --git a/setuptools/depends.py b/setuptools/depends.py index 9f7c9a35..ef3dbb91 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -10,6 +10,7 @@ __all__ = [ 'Require', 'find_module', 'get_module_constant', 'extract_constant' ] + class Require: """A prerequisite to building or installing a distribution""" @@ -39,7 +40,6 @@ class Require: str(version) != "unknown" and version >= self.requested_version def get_version(self, paths=None, default="unknown"): - """Get version number of installed module, 'None', or 'default' Search 'paths' for module. If not found, return 'None'. If found, @@ -78,7 +78,6 @@ class Require: def _iter_code(code): - """Yield '(op,arg)' pair for each operation in code object 'code'""" from array import array @@ -131,7 +130,6 @@ def find_module(module, paths=None): def get_module_constant(module, symbol, default=-1, paths=None): - """Find 'module' by searching 'paths', and extract 'symbol' Return 'None' if 'module' does not exist on 'paths', or it does not define diff --git a/setuptools/dist.py b/setuptools/dist.py index 086e0a58..ee85cf52 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -38,6 +38,7 @@ def _get_unpatched(cls): _Distribution = _get_unpatched(_Distribution) + def _patch_distribution_metadata_write_pkg_info(): """ Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local @@ -61,6 +62,7 @@ _patch_distribution_metadata_write_pkg_info() sequence = tuple, list + def check_importable(dist, attr, value): try: ep = pkg_resources.EntryPoint.parse('x='+value) @@ -80,6 +82,8 @@ def assert_string_list(dist, attr, value): raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr,value) ) + + def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" assert_string_list(dist,attr,value) @@ -97,6 +101,7 @@ def check_nsp(dist, attr, value): " is not: please correct this in setup.py", nsp, parent ) + def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: @@ -113,6 +118,7 @@ def check_extras(dist, attr, value): "requirement specifiers." ) + def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: @@ -131,6 +137,7 @@ def check_requirements(dist, attr, value): ) raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: @@ -138,10 +145,12 @@ def check_entry_points(dist, attr, value): except ValueError as e: raise DistutilsSetupError(e) + def check_test_suite(dist, attr, value): if not isinstance(value, six.string_types): raise DistutilsSetupError("test_suite must be a string") + def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" if isinstance(value,dict): @@ -157,6 +166,7 @@ def check_package_data(dist, attr, value): "wildcard patterns" ) + def check_packages(dist, attr, value): for pkgname in value: if not re.match(r'\w+(\.\w+)*', pkgname): @@ -815,7 +825,6 @@ class Feature: return self.available and self.standard def include_in(self,dist): - """Ensure feature and its requirements are included in distribution You may override this in a subclass to perform additional operations on @@ -836,7 +845,6 @@ class Feature: dist.include_feature(f) def exclude_from(self,dist): - """Ensure feature is excluded from distribution You may override this in a subclass to perform additional operations on @@ -852,7 +860,6 @@ class Feature: dist.exclude_package(item) def validate(self,dist): - """Verify that feature makes sense in context of distribution This method is called by the distribution just before it parses its diff --git a/setuptools/extension.py b/setuptools/extension.py index b9d68178..d265b7a3 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -14,6 +14,7 @@ _Extension = _get_unpatched(distutils.core.Extension) msvc.patch_for_specialized_compiler() + def _have_cython(): """ Return True if Cython can be imported. @@ -48,6 +49,7 @@ class Extension(_Extension): sub = functools.partial(re.sub, '.pyx$', target_ext) self.sources = list(map(sub, self.sources)) + class Library(Extension): """Just like a regular Extension, but built as a library instead""" diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index feef591a..f7296786 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -12,7 +12,9 @@ from distutils import log from lib2to3.refactor import RefactoringTool, get_fixers_from_package import setuptools + class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): log.error(msg, *args) @@ -22,7 +24,9 @@ class DistutilsRefactoringTool(RefactoringTool): def log_debug(self, msg, *args): log.debug(msg, *args) + class Mixin2to3(_Mixin2to3): + def run_2to3(self, files, doctests = False): # See of the distribution option has been set, otherwise check the # setuptools default. diff --git a/setuptools/msvc.py b/setuptools/msvc.py index b543c568..012ab32c 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -769,6 +769,7 @@ class EnvironmentInfo: """ # Variables and properties in this class use originals CamelCase variables # names from Microsoft source files for more easy comparaison. + def __init__(self, arch, vc_ver=None, vc_min_ver=None): self.pi = PlatformInfo(arch) self.ri = RegistryInfo(self.pi) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index e87504db..e9b304b1 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -85,6 +85,7 @@ def egg_info_for_url(url): if '#' in base: base, fragment = base.split('#',1) return base,fragment + def distros_for_url(url, metadata=None): """Yield egg or source distribution objects that might be found at a URL""" base, fragment = egg_info_for_url(url) @@ -97,6 +98,7 @@ def distros_for_url(url, metadata=None): ): yield dist + def distros_for_location(location, basename, metadata=None): """Yield egg or source distribution objects based on basename""" if basename.endswith('.egg.zip'): @@ -118,6 +120,7 @@ def distros_for_location(location, basename, metadata=None): return interpret_distro_name(location, basename, metadata) return [] # no extension matched + def distros_for_filename(filename, metadata=None): """Yield possible egg or source distribution objects based on a filename""" return distros_for_location( @@ -177,6 +180,7 @@ def unique_everseen(iterable, key=None): seen_add(k) yield element + def unique_values(func): """ Wrap a function returning an iterable such that the resulting iterable @@ -190,6 +194,7 @@ def unique_values(func): REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) # this line is here to fix emacs' cruddy broken syntax highlighting + @unique_values def find_external_links(url, page): """Find rel="homepage" and rel="download" links in `page`, yielding URLs""" @@ -213,6 +218,7 @@ class ContentChecker(object): """ A null content checker that defines the interface for checking content """ + def feed(self, block): """ Feed a block of data to the hash. @@ -232,6 +238,7 @@ class ContentChecker(object): """ return + class HashChecker(ContentChecker): pattern = re.compile( r'(?Psha1|sha224|sha384|sha256|sha512|md5)=' @@ -672,6 +679,7 @@ class PackageIndex(Environment): ) dl_blocksize = 8192 + def _download_to(self, url, filename): self.info("Downloading %s", url) # Download the file @@ -883,12 +891,14 @@ class PackageIndex(Environment): # references, a hexadecimal numeric reference, or a named reference). entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub + def uchr(c): if not isinstance(c, int): return c if c>255: return six.unichr(c) return chr(c) + def decode_entity(match): what = match.group(1) if what.startswith('#x'): @@ -899,10 +909,12 @@ def decode_entity(match): what = six.moves.html_entities.name2codepoint.get(what, match.group(0)) return uchr(what) + def htmldecode(text): """Decode HTML entities in the given text.""" return entity_sub(decode_entity, text) + def socket_timeout(timeout=15): def _socket_timeout(func): def _socket_timeout(*args, **kwargs): @@ -915,6 +927,7 @@ def socket_timeout(timeout=15): return _socket_timeout return _socket_timeout + def _encode_auth(auth): """ A function compatible with Python 2.3-3.3 that will encode @@ -937,10 +950,12 @@ def _encode_auth(auth): # strip the trailing carriage return return encoded.replace('\n','') + class Credential(object): """ A username/password pair. Use like a namedtuple. """ + def __init__(self, username, password): self.username = username self.password = password @@ -952,6 +967,7 @@ class Credential(object): def __str__(self): return '%(username)s:%(password)s' % vars(self) + class PyPIConfig(configparser.RawConfigParser): def __init__(self): @@ -1042,6 +1058,7 @@ open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) def fix_sf_url(url): return url # backward compatibility + def local_open(url): """Read a local path, with special support for directories""" scheme, server, path, param, query, frag = urllib.parse.urlparse(url) diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py index 40cbb88e..74ec3980 100644 --- a/setuptools/py26compat.py +++ b/setuptools/py26compat.py @@ -9,6 +9,7 @@ try: except ImportError: from urllib import splittag + def strip_fragment(url): """ In `Python 8280 `_, Python 2.7 and diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 702f7d65..66aecc2a 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -4,6 +4,7 @@ Compatibility Support for Python 2.7 and earlier import sys + def get_all_headers(message, key): """ Given an HTTPMessage, return all headers matching a given key. diff --git a/setuptools/py31compat.py b/setuptools/py31compat.py index 8fe6dd9d..04a314ef 100644 --- a/setuptools/py31compat.py +++ b/setuptools/py31compat.py @@ -8,6 +8,7 @@ try: from sysconfig import get_config_vars, get_path except ImportError: from distutils.sysconfig import get_config_vars, get_python_lib + def get_path(name): if name not in ('platlib', 'purelib'): raise ValueError("Name must be purelib or platlib") @@ -19,12 +20,14 @@ try: except ImportError: import shutil import tempfile + class TemporaryDirectory(object): """ Very simple temporary directory context manager. Will try to delete afterward, but will also ignore OS and similar errors on deletion. """ + def __init__(self): self.name = None # Handle mkdtemp raising an exception self.name = tempfile.mkdtemp() diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 23e296b1..83c3afdb 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -29,6 +29,7 @@ __all__ = [ "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", ] + def _execfile(filename, globals, locals=None): """ Python 3 implementation of execfile. @@ -117,6 +118,7 @@ class ExceptionSaver: A Context Manager that will save an exception, serialized, and restore it later. """ + def __enter__(self): return self @@ -237,6 +239,7 @@ def run_setup(setup_script, args): # reset to include setup dir, w/clean callback list working_set.__init__() working_set.callbacks.append(lambda dist:dist.activate()) + def runner(): ns = dict(__file__=setup_script, __name__='__main__') _execfile(setup_script, ns) @@ -280,6 +283,7 @@ class AbstractSandbox: def _mk_dual_path_wrapper(name): original = getattr(_os,name) + def wrap(self,src,dst,*args,**kw): if self._active: src,dst = self._remap_pair(name,src,dst,*args,**kw) @@ -291,6 +295,7 @@ class AbstractSandbox: def _mk_single_path_wrapper(name, original=None): original = original or getattr(_os,name) + def wrap(self,path,*args,**kw): if self._active: path = self._remap_input(name,path,*args,**kw) @@ -309,6 +314,7 @@ class AbstractSandbox: def _mk_single_with_return(name): original = getattr(_os,name) + def wrap(self,path,*args,**kw): if self._active: path = self._remap_input(name,path,*args,**kw) @@ -321,6 +327,7 @@ class AbstractSandbox: def _mk_query(name): original = getattr(_os,name) + def wrap(self,*args,**kw): retval = original(*args,**kw) if self._active: @@ -364,6 +371,7 @@ except ImportError: # it appears pywin32 is not installed, so no need to exclude. pass + class DirectorySandbox(AbstractSandbox): """Restrict operations to a single subdirectory - pseudo-chroot""" @@ -453,6 +461,7 @@ WRITE_FLAGS = functools.reduce( "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] ) + class SandboxViolation(DistutilsError): """A setup script attempted to modify the filesystem outside the sandbox""" @@ -468,29 +477,4 @@ script by hand. Please inform the package's author and the EasyInstall maintainers to find out if a fix or workaround is available.""" % self.args - - - - - - - - - - - - - - - - - - - - - - - - - # diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 657197cf..ecede509 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -161,6 +161,7 @@ class VerifyingHTTPSHandler(HTTPSHandler): class VerifyingHTTPSConn(HTTPSConnection): """Simple verifying connection: no auth, subclasses, timeouts, etc.""" + def __init__(self, host, ca_bundle, **kw): HTTPSConnection.__init__(self, host, **kw) self.ca_bundle = ca_bundle @@ -192,6 +193,7 @@ class VerifyingHTTPSConn(HTTPSConnection): self.sock.close() raise + def opener_for(ca_bundle=None): """Get a urlopen() replacement that uses ca_bundle for verification""" return urllib.request.build_opener( @@ -201,6 +203,7 @@ def opener_for(ca_bundle=None): _wincerts = None + def get_win_certfile(): global _wincerts if _wincerts is not None: @@ -212,6 +215,7 @@ def get_win_certfile(): return None class MyCertFile(CertFile): + def __init__(self, stores=(), certs=()): CertFile.__init__(self) for store in stores: diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 32447356..569b060f 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -40,6 +40,7 @@ needs_bytecode = pytest.mark.skipif( reason="bytecode support not available", ) + class TestDepends: def testExtractConst(self): @@ -289,6 +290,7 @@ class TestFeatures: with pytest.raises(SystemExit): makeSetup(features={'x':Feature('x', remove='y')}) + class TestCommandTests: def testTestIsCommand(self): diff --git a/setuptools/tests/py26compat.py b/setuptools/tests/py26compat.py index 7211f275..5325dad6 100644 --- a/setuptools/tests/py26compat.py +++ b/setuptools/tests/py26compat.py @@ -2,6 +2,7 @@ import sys import tarfile import contextlib + def _tarfile_open_ex(*args, **kwargs): """ Extend result as a context manager. diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py index 6a687937..9e5fefb7 100644 --- a/setuptools/tests/server.py +++ b/setuptools/tests/server.py @@ -18,6 +18,7 @@ class IndexServer(BaseHTTPServer.HTTPServer): # The index files should be located in setuptools/tests/indexes s.stop() """ + def __init__(self, server_address=('', 0), RequestHandlerClass=SimpleHTTPServer.SimpleHTTPRequestHandler): BaseHTTPServer.HTTPServer.__init__(self, server_address, @@ -42,16 +43,20 @@ class IndexServer(BaseHTTPServer.HTTPServer): port = self.server_port return 'http://127.0.0.1:%s/setuptools/tests/indexes/' % port + class RequestRecorder(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(self): requests = vars(self.server).setdefault('requests', []) requests.append(self) self.send_response(200, 'OK') + class MockServer(BaseHTTPServer.HTTPServer, threading.Thread): """ A simple HTTP Server that records the requests made to it. """ + def __init__(self, server_address=('', 0), RequestHandlerClass=RequestRecorder): BaseHTTPServer.HTTPServer.__init__(self, server_address, diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index ccfb2ea7..a7ceac86 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -15,6 +15,7 @@ from setuptools import setup setup(name='foo', py_modules=['hi']) """ + @pytest.yield_fixture def setup_context(tmpdir): with (tmpdir/'setup.py').open('w') as f: diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 0719ba44..5168ebf0 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -3,7 +3,9 @@ import distutils.command.build_ext as orig from setuptools.command.build_ext import build_ext from setuptools.dist import Distribution + class TestBuildExt: + def test_get_ext_filename(self): """ Setuptools needs to give back the same diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 1b844499..d22e5e4a 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -26,6 +26,7 @@ setup(name='foo', INIT_PY = """print "foo" """ + @pytest.yield_fixture def temp_user(monkeypatch): with contexts.tempdir() as user_base: @@ -54,6 +55,7 @@ def test_env(tmpdir, temp_user): class TestDevelop: in_virtualenv = hasattr(sys, 'real_prefix') in_venv = hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix + @pytest.mark.skipif(in_virtualenv or in_venv, reason="Cannot run when invoked in a virtualenv or venv") def test_2to3_user_mode(self, test_env): diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index fd06b6ef..894c4fd8 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -41,6 +41,7 @@ from .textwrap import DALS class FakeDist(object): + def get_entry_map(self, group): if group != 'console_scripts': return {} @@ -55,6 +56,7 @@ SETUP_PY = DALS(""" setup(name='foo') """) + class TestEasyInstallTest: def test_install_site_py(self, tmpdir): @@ -133,6 +135,7 @@ class TestEasyInstallTest: class TestPTHFileWriter: + def test_add_from_cwd_site_sets_dirty(self): '''a pth file manager should set dirty if a distribution is in site but also the cwd @@ -266,6 +269,7 @@ def distutils_package(): class TestDistutilsPackage: + def test_bdist_egg_available_on_distutils_pkg(self, distutils_package): run_setup('setup.py', ['bdist_egg']) @@ -557,6 +561,7 @@ class TestScriptHeader: class TestCommandSpec: + def test_custom_launch_command(self): """ Show how a custom CommandSpec could be used to specify a #! executable @@ -601,6 +606,7 @@ class TestCommandSpec: class TestWindowsScriptWriter: + def test_header(self): hdr = ei.WindowsScriptWriter.get_script_header('') assert hdr.startswith('#!') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 3a0db58f..afbda2cc 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -235,6 +235,7 @@ class TestEggInfo(object): def _find_egg_info_files(self, root): class DirList(list): + def __init__(self, files, base): super(DirList, self).__init__(files) self.base = base diff --git a/setuptools/tests/test_find_packages.py b/setuptools/tests/test_find_packages.py index 06a7c02e..65e7243d 100644 --- a/setuptools/tests/test_find_packages.py +++ b/setuptools/tests/test_find_packages.py @@ -13,6 +13,8 @@ from setuptools import find_packages find_420_packages = setuptools.PEP420PackageFinder.find # modeled after CPython's test.support.can_symlink + + def can_symlink(): TESTFN = tempfile.mktemp() symlink_path = TESTFN + "can_symlink" @@ -26,6 +28,7 @@ def can_symlink(): globals().update(can_symlink=lambda: can) return can + def has_symlink(): bad_symlink = ( # Windows symlink directory detection is broken on Python 3.2 @@ -33,6 +36,7 @@ def has_symlink(): ) return can_symlink() and not bad_symlink + class TestFindPackages: def setup_method(self, method): diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 6a76b5fc..61f5909b 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -209,6 +209,7 @@ class TestContentCheckers: class TestPyPIConfig: + def test_percent_in_password(self, tmpdir, monkeypatch): monkeypatch.setitem(os.environ, 'HOME', str(tmpdir)) pypirc = tmpdir / '.pypirc' diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py index fefd46f7..aa6138e4 100644 --- a/setuptools/tests/test_sandbox.py +++ b/setuptools/tests/test_sandbox.py @@ -57,6 +57,7 @@ class TestSandbox: class TestExceptionSaver: + def test_exception_trapped(self): with setuptools.sandbox.ExceptionSaver(): raise ValueError("details") diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index d2a1f1bb..16d0eb07 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -132,7 +132,6 @@ class TestSdistTest: assert os.path.join('sdist_test', 'b.txt') in manifest assert os.path.join('sdist_test', 'c.rst') not in manifest - def test_defaults_case_sensitivity(self): """ Make sure default files (README.*, etc.) are added in a case-sensitive diff --git a/tests/manual_test.py b/tests/manual_test.py index 808fa55a..0904b607 100644 --- a/tests/manual_test.py +++ b/tests/manual_test.py @@ -10,9 +10,11 @@ from string import Template from six.moves import urllib + def _system_call(*args): assert subprocess.call(args) == 0 + def tempdir(func): def _tempdir(*args, **kwargs): test_dir = tempfile.mkdtemp() @@ -62,6 +64,7 @@ def test_virtualenv(): res = f.read() assert 'setuptools' in res + @tempdir def test_full(): """virtualenv + pip + buildout""" -- cgit v1.2.1 From 7ba7d1b9737c95e7dc77c0379a81f886d23653a4 Mon Sep 17 00:00:00 2001 From: stepshal Date: Wed, 13 Jul 2016 11:53:42 +0700 Subject: Fix comparison with None. --- setuptools/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 32447356..87dc8f54 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -325,4 +325,4 @@ class TestCommandTests: def testNoSuite(self): ts5 = makeSetup().get_command_obj('test') ts5.ensure_finalized() - assert ts5.test_suite == None + assert ts5.test_suite is None -- cgit v1.2.1 From f1e3a3fc4199c80e1eca7af0d1bd10544faf1542 Mon Sep 17 00:00:00 2001 From: stepshal Date: Wed, 13 Jul 2016 14:49:35 +0700 Subject: Format block comments. --- setuptools/site-patch.py | 4 ++-- setuptools/tests/environment.py | 8 ++++---- setuptools/tests/test_build_py.py | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py index c2168019..bb887ad0 100644 --- a/setuptools/site-patch.py +++ b/setuptools/site-patch.py @@ -10,7 +10,7 @@ def __boot(): pic = getattr(sys,'path_importer_cache',{}) stdpath = sys.path[len(PYTHONPATH):] mydir = os.path.dirname(__file__) - #print "searching",stdpath,sys.path + # print "searching",stdpath,sys.path for item in stdpath: if item==mydir or not item: @@ -39,7 +39,7 @@ def __boot(): else: raise ImportError("Couldn't find the real 'site' module") - #print "loaded", __file__ + # print "loaded", __file__ known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py index a23c0504..e5237721 100644 --- a/setuptools/tests/environment.py +++ b/setuptools/tests/environment.py @@ -25,11 +25,11 @@ def run_setup_py(cmd, pypath=None, path=None, for envname in os.environ: env[envname] = os.environ[envname] - #override the python path if needed + # override the python path if needed if pypath is not None: env["PYTHONPATH"] = pypath - #overide the execution path if needed + # overide the execution path if needed if path is not None: env["PATH"] = path if not env.get("PATH", ""): @@ -50,11 +50,11 @@ def run_setup_py(cmd, pypath=None, path=None, except OSError: return 1, '' - #decode the console string if needed + # decode the console string if needed if hasattr(data, "decode"): # use the default encoding data = data.decode() data = unicodedata.normalize('NFC', data) - #communciate calls wait() + # communciate calls wait() return proc.returncode, data diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py index ed1703ac..860c569c 100644 --- a/setuptools/tests/test_build_py.py +++ b/setuptools/tests/test_build_py.py @@ -26,6 +26,6 @@ def test_directories_in_package_data_glob(tmpdir_as_cwd): package_data={'': ['path/*']}, )) os.makedirs('path/subpath') - #with contexts.quiet(): + # with contexts.quiet(): dist.parse_command_line() dist.run_commands() -- cgit v1.2.1 From c064f573cd7dcfd684afe9fb6d827afb6c3bd363 Mon Sep 17 00:00:00 2001 From: stepshal Date: Wed, 13 Jul 2016 15:16:47 +0700 Subject: tests: match closing bracket for visual identation --- setuptools/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 32447356..02e5b9bc 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -221,7 +221,7 @@ class TestFeatures: 'foo': Feature("foo",standard=True,require_features=['baz',self.req]), 'bar': Feature("bar", standard=True, packages=['pkg.bar'], py_modules=['bar_et'], remove=['bar.ext'], - ), + ), 'baz': Feature( "baz", optional=False, packages=['pkg.baz'], scripts = ['scripts/baz_it'], -- cgit v1.2.1 From 053a3a12cf0cc902e0f869b8cc4cff997f73fc84 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 06:59:30 +0700 Subject: Add missing blank line. --- setuptools/package_index.py | 1 + setuptools/unicode_utils.py | 1 + 2 files changed, 2 insertions(+) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index e9b304b1..7fb8b954 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -162,6 +162,7 @@ def interpret_distro_name( platform = platform ) + # From Python 2.7 docs def unique_everseen(iterable, key=None): "List unique elements, preserving order. Remember all elements ever seen." diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index ffab3e24..7c63efd2 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -3,6 +3,7 @@ import sys from setuptools.extern import six + # HFS Plus uses decomposed UTF-8 def decompose(path): if isinstance(path, six.text_type): -- cgit v1.2.1 From f749ccab1e55723848946c9aba5c3eddebe0b24e Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 09:26:06 +0700 Subject: Add missing whitespace. --- setuptools/archive_util.py | 2 +- setuptools/depends.py | 12 +-- setuptools/dist.py | 136 ++++++++++++++++----------------- setuptools/package_index.py | 74 +++++++++--------- setuptools/py26compat.py | 2 +- setuptools/sandbox.py | 68 ++++++++--------- setuptools/site-patch.py | 10 +-- setuptools/tests/__init__.py | 72 ++++++++--------- setuptools/tests/contexts.py | 2 +- setuptools/tests/test_find_packages.py | 2 +- 10 files changed, 190 insertions(+), 190 deletions(-) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index 4da24ec2..d1950638 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -20,7 +20,7 @@ class UnrecognizedFormat(DistutilsError): """Couldn't recognize the archive type""" -def default_filter(src,dst): +def default_filter(src, dst): """The default progress/filter callback; returns True for all files""" return dst diff --git a/setuptools/depends.py b/setuptools/depends.py index ef3dbb91..eb1d7b13 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -31,7 +31,7 @@ class Require: def full_name(self): """Return full package/distribution name, w/version""" if self.requested_version is not None: - return '%s-%s' % (self.name,self.requested_version) + return '%s-%s' % (self.name, self.requested_version) return self.name def version_ok(self, version): @@ -52,7 +52,7 @@ class Require: if self.attribute is None: try: - f,p,i = find_module(self.module,paths) + f, p, i = find_module(self.module, paths) if f: f.close() return default except ImportError: @@ -83,7 +83,7 @@ def _iter_code(code): from array import array from dis import HAVE_ARGUMENT, EXTENDED_ARG - bytes = array('b',code.co_code) + bytes = array('b', code.co_code) eof = len(code.co_code) ptr = 0 @@ -107,7 +107,7 @@ def _iter_code(code): arg = None ptr += 1 - yield op,arg + yield op, arg def find_module(module, paths=None): @@ -117,14 +117,14 @@ def find_module(module, paths=None): while parts: part = parts.pop(0) - f, path, (suffix,mode,kind) = info = imp.find_module(part, paths) + f, path, (suffix, mode, kind) = info = imp.find_module(part, paths) if kind==PKG_DIRECTORY: parts = parts or ['__init__'] paths = [path] elif parts: - raise ImportError("Can't find %r in %s" % (parts,module)) + raise ImportError("Can't find %r in %s" % (parts, module)) return info diff --git a/setuptools/dist.py b/setuptools/dist.py index ee85cf52..f229d726 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -67,10 +67,10 @@ def check_importable(dist, attr, value): try: ep = pkg_resources.EntryPoint.parse('x='+value) assert not ep.extras - except (TypeError,ValueError,AttributeError,AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( "%r must be importable 'module:attrs' string (got %r)" - % (attr,value) + % (attr, value) ) @@ -78,15 +78,15 @@ def assert_string_list(dist, attr, value): """Verify that value is a string list or None""" try: assert ''.join(value)!=value - except (TypeError,ValueError,AttributeError,AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr,value) + "%r must be a list of strings (got %r)" % (attr, value) ) def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" - assert_string_list(dist,attr,value) + assert_string_list(dist, attr, value) for nsp in value: if not dist.has_contents_for(nsp): raise DistutilsSetupError( @@ -105,13 +105,13 @@ def check_nsp(dist, attr, value): def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: - for k,v in value.items(): + for k, v in value.items(): if ':' in k: - k,m = k.split(':',1) + k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): raise DistutilsSetupError("Invalid environment marker: "+m) list(pkg_resources.parse_requirements(v)) - except (TypeError,ValueError,AttributeError): + except (TypeError, ValueError, AttributeError): raise DistutilsSetupError( "'extras_require' must be a dictionary whose values are " "strings or lists of strings containing valid project/version " @@ -153,9 +153,9 @@ def check_test_suite(dist, attr, value): def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" - if isinstance(value,dict): - for k,v in value.items(): - if not isinstance(k,str): break + if isinstance(value, dict): + for k, v in value.items(): + if not isinstance(k, str): break try: iter(v) except TypeError: break @@ -274,12 +274,12 @@ class Distribution(_Distribution): # Make sure we have any eggs needed to interpret 'attrs' if attrs is not None: self.dependency_links = attrs.pop('dependency_links', []) - assert_string_list(self,'dependency_links',self.dependency_links) + assert_string_list(self, 'dependency_links', self.dependency_links) if attrs and 'setup_requires' in attrs: self.fetch_build_eggs(attrs['setup_requires']) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) - _Distribution.__init__(self,attrs) + _Distribution.__init__(self, attrs) if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) @@ -311,9 +311,9 @@ class Distribution(_Distribution): self._finalize_features() return result - def _feature_attrname(self,name): + def _feature_attrname(self, name): """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-','_') + return 'with_'+name.replace('-', '_') def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" @@ -331,7 +331,7 @@ class Distribution(_Distribution): self._set_global_opts_from_features() for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - value = getattr(self,ep.name,None) + value = getattr(self, ep.name, None) if value is not None: ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) @@ -364,7 +364,7 @@ class Distribution(_Distribution): cmd.package_index.to_scan = [] except AttributeError: from setuptools.command.easy_install import easy_install - dist = self.__class__({'script_args':['easy_install']}) + dist = self.__class__({'script_args': ['easy_install']}) dist.parse_config_files() opts = dist.get_option_dict('easy_install') keep = ( @@ -395,8 +395,8 @@ class Distribution(_Distribution): go = [] no = self.negative_opt.copy() - for name,feature in self.features.items(): - self._set_feature(name,None) + for name, feature in self.features.items(): + self._set_feature(name, None) feature.validate(self) if feature.optional: @@ -417,25 +417,25 @@ class Distribution(_Distribution): """Add/remove features and resolve dependencies between them""" # First, flag all the enabled items (and thus their dependencies) - for name,feature in self.features.items(): + for name, feature in self.features.items(): enabled = self.feature_is_included(name) if enabled or (enabled is None and feature.include_by_default()): feature.include_in(self) - self._set_feature(name,1) + self._set_feature(name, 1) # Then disable the rest, so that off-by-default features don't # get flagged as errors when they're required by an enabled feature - for name,feature in self.features.items(): + for name, feature in self.features.items(): if not self.feature_is_included(name): feature.exclude_from(self) - self._set_feature(name,0) + self._set_feature(name, 0) def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: return self.cmdclass[command] - for ep in pkg_resources.iter_entry_points('distutils.commands',command): + for ep in pkg_resources.iter_entry_points('distutils.commands', command): ep.require(installer=self.fetch_build_egg) self.cmdclass[command] = cmdclass = ep.load() return cmdclass @@ -458,15 +458,15 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.get_command_list(self) - def _set_feature(self,name,status): + def _set_feature(self, name, status): """Set feature's inclusion status""" - setattr(self,self._feature_attrname(name),status) + setattr(self, self._feature_attrname(name), status) - def feature_is_included(self,name): + def feature_is_included(self, name): """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self,self._feature_attrname(name)) + return getattr(self, self._feature_attrname(name)) - def include_feature(self,name): + def include_feature(self, name): """Request inclusion of feature named 'name'""" if self.feature_is_included(name)==0: @@ -475,9 +475,9 @@ class Distribution(_Distribution): descr + " is required, but was excluded or is not available" ) self.features[name].include_in(self) - self._set_feature(name,1) + self._set_feature(name, 1) - def include(self,**attrs): + def include(self, **attrs): """Add items to distribution that are named in keyword arguments For example, 'dist.exclude(py_modules=["x"])' would add 'x' to @@ -492,14 +492,14 @@ class Distribution(_Distribution): will try to call 'dist._include_foo({"bar":"baz"})', which can then handle whatever special inclusion logic is needed. """ - for k,v in attrs.items(): + for k, v in attrs.items(): include = getattr(self, '_include_'+k, None) if include: include(v) else: - self._include_misc(k,v) + self._include_misc(k, v) - def exclude_package(self,package): + def exclude_package(self, package): """Remove packages, modules, and extensions in named package""" pfx = package+'.' @@ -521,7 +521,7 @@ class Distribution(_Distribution): if p.name != package and not p.name.startswith(pfx) ] - def has_contents_for(self,package): + def has_contents_for(self, package): """Return true if 'exclude_package(package)' would do something""" pfx = package+'.' @@ -530,48 +530,48 @@ class Distribution(_Distribution): if p==package or p.startswith(pfx): return True - def _exclude_misc(self,name,value): + def _exclude_misc(self, name, value): """Handle 'exclude()' for list/tuple attrs without a special handler""" - if not isinstance(value,sequence): + if not isinstance(value, sequence): raise DistutilsSetupError( "%s: setting must be a list or tuple (%r)" % (name, value) ) try: - old = getattr(self,name) + old = getattr(self, name) except AttributeError: raise DistutilsSetupError( "%s: No such distribution setting" % name ) - if old is not None and not isinstance(old,sequence): + if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name+": this setting cannot be changed via include/exclude" ) elif old: - setattr(self,name,[item for item in old if item not in value]) + setattr(self, name, [item for item in old if item not in value]) - def _include_misc(self,name,value): + def _include_misc(self, name, value): """Handle 'include()' for list/tuple attrs without a special handler""" - if not isinstance(value,sequence): + if not isinstance(value, sequence): raise DistutilsSetupError( "%s: setting must be a list (%r)" % (name, value) ) try: - old = getattr(self,name) + old = getattr(self, name) except AttributeError: raise DistutilsSetupError( "%s: No such distribution setting" % name ) if old is None: - setattr(self,name,value) - elif not isinstance(old,sequence): + setattr(self, name, value) + elif not isinstance(old, sequence): raise DistutilsSetupError( name+": this setting cannot be changed via include/exclude" ) else: - setattr(self,name,old+[item for item in value if item not in old]) + setattr(self, name, old+[item for item in value if item not in old]) - def exclude(self,**attrs): + def exclude(self, **attrs): """Remove items from distribution that are named in keyword arguments For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from @@ -587,15 +587,15 @@ class Distribution(_Distribution): will try to call 'dist._exclude_foo({"bar":"baz"})', which can then handle whatever special exclusion logic is needed. """ - for k,v in attrs.items(): + for k, v in attrs.items(): exclude = getattr(self, '_exclude_'+k, None) if exclude: exclude(v) else: - self._exclude_misc(k,v) + self._exclude_misc(k, v) - def _exclude_packages(self,packages): - if not isinstance(packages,sequence): + def _exclude_packages(self, packages): + if not isinstance(packages, sequence): raise DistutilsSetupError( "packages: setting must be a list or tuple (%r)" % (packages,) ) @@ -610,17 +610,17 @@ class Distribution(_Distribution): command = args[0] aliases = self.get_option_dict('aliases') while command in aliases: - src,alias = aliases[command] + src, alias = aliases[command] del aliases[command] # ensure each alias can expand only once! import shlex - args[:1] = shlex.split(alias,True) + args[:1] = shlex.split(alias, True) command = args[0] nargs = _Distribution._parse_command_opts(self, parser, args) # Handle commands that want to consume all remaining arguments cmd_class = self.get_command_class(command) - if getattr(cmd_class,'command_consumes_arguments',None): + if getattr(cmd_class, 'command_consumes_arguments', None): self.get_option_dict(command)['args'] = ("command line", nargs) if nargs is not None: return [] @@ -639,20 +639,20 @@ class Distribution(_Distribution): d = {} - for cmd,opts in self.command_options.items(): + for cmd, opts in self.command_options.items(): - for opt,(src,val) in opts.items(): + for opt, (src, val) in opts.items(): if src != "command line": continue - opt = opt.replace('_','-') + opt = opt.replace('_', '-') if val==0: cmdobj = self.get_command_obj(cmd) neg_opt = self.negative_opt.copy() - neg_opt.update(getattr(cmdobj,'negative_opt',{})) - for neg,pos in neg_opt.items(): + neg_opt.update(getattr(cmdobj, 'negative_opt', {})) + for neg, pos in neg_opt.items(): if pos==opt: opt=neg val=None @@ -663,7 +663,7 @@ class Distribution(_Distribution): elif val==1: val = None - d.setdefault(cmd,{})[opt] = val + d.setdefault(cmd, {})[opt] = val return d @@ -677,7 +677,7 @@ class Distribution(_Distribution): yield module for ext in self.ext_modules or (): - if isinstance(ext,tuple): + if isinstance(ext, tuple): name, buildinfo = ext else: name = ext.name @@ -800,16 +800,16 @@ class Feature: self.standard = standard self.available = available self.optional = optional - if isinstance(require_features,(str,Require)): + if isinstance(require_features, (str, Require)): require_features = require_features, self.require_features = [ - r for r in require_features if isinstance(r,str) + r for r in require_features if isinstance(r, str) ] - er = [r for r in require_features if not isinstance(r,str)] + er = [r for r in require_features if not isinstance(r, str)] if er: extras['require_features'] = er - if isinstance(remove,str): + if isinstance(remove, str): remove = remove, self.remove = remove self.extras = extras @@ -824,7 +824,7 @@ class Feature: """Should this feature be included by default?""" return self.available and self.standard - def include_in(self,dist): + def include_in(self, dist): """Ensure feature and its requirements are included in distribution You may override this in a subclass to perform additional operations on @@ -844,7 +844,7 @@ class Feature: for f in self.require_features: dist.include_feature(f) - def exclude_from(self,dist): + def exclude_from(self, dist): """Ensure feature is excluded from distribution You may override this in a subclass to perform additional operations on @@ -859,7 +859,7 @@ class Feature: for item in self.remove: dist.exclude_package(item) - def validate(self,dist): + def validate(self, dist): """Verify that feature makes sense in context of distribution This method is called by the distribution just before it parses its diff --git a/setuptools/package_index.py b/setuptools/package_index.py index e9b304b1..f3ee5ec0 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -37,7 +37,7 @@ PYPI_MD5 = re.compile( '([^<]+)\n\s+\\(md5\\)' ) -URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match +URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() __all__ = [ @@ -62,18 +62,18 @@ def parse_bdist_wininst(name): if lower.endswith('.win32.exe'): base = name[:-10] plat = 'win32' - elif lower.startswith('.win32-py',-16): + elif lower.startswith('.win32-py', -16): py_ver = name[-7:-4] base = name[:-16] plat = 'win32' elif lower.endswith('.win-amd64.exe'): base = name[:-14] plat = 'win-amd64' - elif lower.startswith('.win-amd64-py',-20): + elif lower.startswith('.win-amd64-py', -20): py_ver = name[-7:-4] base = name[:-20] plat = 'win-amd64' - return base,py_ver,plat + return base, py_ver, plat def egg_info_for_url(url): @@ -82,8 +82,8 @@ def egg_info_for_url(url): base = urllib.parse.unquote(path.split('/')[-1]) if server=='sourceforge.net' and base=='download': # XXX Yuck base = urllib.parse.unquote(path.split('/')[-2]) - if '#' in base: base, fragment = base.split('#',1) - return base,fragment + if '#' in base: base, fragment = base.split('#', 1) + return base, fragment def distros_for_url(url, metadata=None): @@ -155,7 +155,7 @@ def interpret_distro_name( # it is a bdist_dumb, not an sdist -- bail out return - for p in range(1,len(parts)+1): + for p in range(1, len(parts)+1): yield Distribution( location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), py_version=py_version, precedence = precedence, @@ -209,7 +209,7 @@ def find_external_links(url, page): for tag in ("Home Page", "Download URL"): pos = page.find(tag) if pos!=-1: - match = HREF.search(page,pos) + match = HREF.search(page, pos) if match: yield urllib.parse.urljoin(url, htmldecode(match.group(1))) @@ -279,12 +279,12 @@ class PackageIndex(Environment): self, index_url="https://pypi.python.org/simple", hosts=('*',), ca_bundle=None, verify_ssl=True, *args, **kw ): - Environment.__init__(self,*args,**kw) + Environment.__init__(self, *args, **kw) self.index_url = index_url + "/"[:not index_url.endswith('/')] self.scanned_urls = {} self.fetched_urls = {} self.package_pages = {} - self.allows = re.compile('|'.join(map(translate,hosts))).match + self.allows = re.compile('|'.join(map(translate, hosts))).match self.to_scan = [] if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()): self.opener = ssl_support.opener_for(ca_bundle) @@ -335,7 +335,7 @@ class PackageIndex(Environment): for match in HREF.finditer(page): link = urllib.parse.urljoin(base, htmldecode(match.group(1))) self.process_url(link) - if url.startswith(self.index_url) and getattr(f,'code',None)!=404: + if url.startswith(self.index_url) and getattr(f, 'code', None)!=404: page = self.process_index(url, page) def process_filename(self, fn, nested=False): @@ -347,7 +347,7 @@ class PackageIndex(Environment): if os.path.isdir(fn) and not nested: path = os.path.realpath(fn) for item in os.listdir(path): - self.process_filename(os.path.join(path,item), True) + self.process_filename(os.path.join(path, item), True) dists = distros_for_filename(fn) if dists: @@ -391,7 +391,7 @@ class PackageIndex(Environment): dist.precedence = SOURCE_DIST self.add(dist) - def process_index(self,url,page): + def process_index(self, url, page): """Process the contents of a PyPI page""" def scan(link): # Process a URL to see if it's for a package page @@ -403,7 +403,7 @@ class PackageIndex(Environment): # it's a package page, sanitize and index it pkg = safe_name(parts[0]) ver = safe_version(parts[1]) - self.package_pages.setdefault(pkg.lower(),{})[link] = True + self.package_pages.setdefault(pkg.lower(), {})[link] = True return to_filename(pkg), to_filename(ver) return None, None @@ -422,13 +422,13 @@ class PackageIndex(Environment): base, frag = egg_info_for_url(new_url) if base.endswith('.py') and not frag: if ver: - new_url+='#egg=%s-%s' % (pkg,ver) + new_url+='#egg=%s-%s' % (pkg, ver) else: self.need_version_info(url) self.scan_url(new_url) return PYPI_MD5.sub( - lambda m: '%s' % m.group(1,3,2), page + lambda m: '%s' % m.group(1, 3, 2), page ) else: return "" # no sense double-scanning non-package pages @@ -441,7 +441,7 @@ class PackageIndex(Environment): def scan_all(self, msg=None, *args): if self.index_url not in self.fetched_urls: - if msg: self.warn(msg,*args) + if msg: self.warn(msg, *args) self.info( "Scanning index of all packages (this may take a while)" ) @@ -458,7 +458,7 @@ class PackageIndex(Environment): # We couldn't find the target package, so search the index page too self.not_found_in_index(requirement) - for url in list(self.package_pages.get(requirement.key,())): + for url in list(self.package_pages.get(requirement.key, ())): # scan each page that might be related to the desired package self.scan_url(url) @@ -469,7 +469,7 @@ class PackageIndex(Environment): if dist in requirement: return dist self.debug("%s does not match %s", requirement, dist) - return super(PackageIndex, self).obtain(requirement,installer) + return super(PackageIndex, self).obtain(requirement, installer) def check_hash(self, checker, filename, tfp): """ @@ -534,14 +534,14 @@ class PackageIndex(Environment): of `tmpdir`, and the local filename is returned. Various errors may be raised if a problem occurs during downloading. """ - if not isinstance(spec,Requirement): + if not isinstance(spec, Requirement): scheme = URL_SCHEME(spec) if scheme: # It's a url, download it to tmpdir found = self._download_url(scheme.group(1), spec, tmpdir) base, fragment = egg_info_for_url(spec) if base.endswith('.py'): - found = self.gen_setup(found,fragment,tmpdir) + found = self.gen_setup(found, fragment, tmpdir) return found elif os.path.exists(spec): # Existing file or directory, just return it @@ -554,7 +554,7 @@ class PackageIndex(Environment): "Not a URL, existing file, or requirement spec: %r" % (spec,) ) - return getattr(self.fetch_distribution(spec, tmpdir),'location',None) + return getattr(self.fetch_distribution(spec, tmpdir), 'location', None) def fetch_distribution( self, requirement, tmpdir, force_scan=False, source=False, @@ -590,7 +590,7 @@ class PackageIndex(Environment): if dist.precedence==DEVELOP_DIST and not develop_ok: if dist not in skipped: - self.warn("Skipping development or system egg: %s",dist) + self.warn("Skipping development or system egg: %s", dist) skipped[dist] = 1 continue @@ -632,7 +632,7 @@ class PackageIndex(Environment): ``location`` of the downloaded distribution instead of a distribution object. """ - dist = self.fetch_distribution(requirement,tmpdir,force_scan,source) + dist = self.fetch_distribution(requirement, tmpdir, force_scan, source) if dist is not None: return dist.location return None @@ -670,7 +670,7 @@ class PackageIndex(Environment): raise DistutilsError( "Can't unambiguously interpret project/version identifier %r; " "any dashes in the name or version should be escaped using " - "underscores. %r" % (fragment,dists) + "underscores. %r" % (fragment, dists) ) else: raise DistutilsError( @@ -689,7 +689,7 @@ class PackageIndex(Environment): fp = self.open_url(strip_fragment(url)) if isinstance(fp, urllib.error.HTTPError): raise DistutilsError( - "Can't download %s: %s %s" % (url, fp.code,fp.msg) + "Can't download %s: %s %s" % (url, fp.code, fp.msg) ) headers = fp.info() blocknum = 0 @@ -700,7 +700,7 @@ class PackageIndex(Environment): sizes = get_all_headers(headers, 'Content-Length') size = max(map(int, sizes)) self.reporthook(url, filename, blocknum, bs, size) - with open(filename,'wb') as tfp: + with open(filename, 'wb') as tfp: while True: block = fp.read(bs) if block: @@ -759,14 +759,14 @@ class PackageIndex(Environment): name, fragment = egg_info_for_url(url) if name: while '..' in name: - name = name.replace('..','.').replace('\\','_') + name = name.replace('..', '.').replace('\\', '_') else: name = "__downloaded__" # default if URL has no path contents if name.endswith('.egg.zip'): name = name[:-4] # strip the extra .zip before download - filename = os.path.join(tmpdir,name) + filename = os.path.join(tmpdir, name) # Download the file # @@ -787,7 +787,7 @@ class PackageIndex(Environment): def _attempt_download(self, url, filename): headers = self._download_to(url, filename) - if 'html' in headers.get('content-type','').lower(): + if 'html' in headers.get('content-type', '').lower(): return self._download_html(url, headers, filename) else: return filename @@ -808,16 +808,16 @@ class PackageIndex(Environment): raise DistutilsError("Unexpected HTML page found at "+url) def _download_svn(self, url, filename): - url = url.split('#',1)[0] # remove any fragment for svn's sake + url = url.split('#', 1)[0] # remove any fragment for svn's sake creds = '' if url.lower().startswith('svn:') and '@' in url: scheme, netloc, path, p, q, f = urllib.parse.urlparse(url) if not netloc and path.startswith('//') and '/' in path[2:]: - netloc, path = path[2:].split('/',1) + netloc, path = path[2:].split('/', 1) auth, host = splituser(netloc) if auth: if ':' in auth: - user, pw = auth.split(':',1) + user, pw = auth.split(':', 1) creds = " --username=%s --password=%s" % (user, pw) else: creds = " --username="+auth @@ -835,7 +835,7 @@ class PackageIndex(Environment): scheme = scheme.split('+', 1)[-1] # Some fragment identification fails - path = path.split('#',1)[0] + path = path.split('#', 1)[0] rev = None if '@' in path: @@ -847,7 +847,7 @@ class PackageIndex(Environment): return url, rev def _download_git(self, url, filename): - filename = filename.split('#',1)[0] + filename = filename.split('#', 1)[0] url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) self.info("Doing git clone from %s to %s", url, filename) @@ -863,7 +863,7 @@ class PackageIndex(Environment): return filename def _download_hg(self, url, filename): - filename = filename.split('#',1)[0] + filename = filename.split('#', 1)[0] url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) self.info("Doing hg clone from %s to %s", url, filename) @@ -948,7 +948,7 @@ def _encode_auth(auth): # convert back to a string encoded = encoded_bytes.decode() # strip the trailing carriage return - return encoded.replace('\n','') + return encoded.replace('\n', '') class Credential(object): diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py index 74ec3980..7c60c90e 100644 --- a/setuptools/py26compat.py +++ b/setuptools/py26compat.py @@ -19,5 +19,5 @@ def strip_fragment(url): url, fragment = splittag(url) return url -if sys.version_info >= (2,7): +if sys.version_info >= (2, 7): strip_fragment = lambda x: x diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 83c3afdb..c6bab096 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -238,7 +238,7 @@ def run_setup(setup_script, args): sys.path.insert(0, setup_dir) # reset to include setup dir, w/clean callback list working_set.__init__() - working_set.callbacks.append(lambda dist:dist.activate()) + working_set.callbacks.append(lambda dist: dist.activate()) def runner(): ns = dict(__file__=setup_script, __name__='__main__') @@ -258,12 +258,12 @@ class AbstractSandbox: def __init__(self): self._attrs = [ name for name in dir(_os) - if not name.startswith('_') and hasattr(self,name) + if not name.startswith('_') and hasattr(self, name) ] def _copy(self, source): for name in self._attrs: - setattr(os, name, getattr(source,name)) + setattr(os, name, getattr(source, name)) def run(self, func): """Run 'func' under os sandboxing""" @@ -282,24 +282,24 @@ class AbstractSandbox: self._copy(_os) def _mk_dual_path_wrapper(name): - original = getattr(_os,name) + original = getattr(_os, name) - def wrap(self,src,dst,*args,**kw): + def wrap(self, src, dst, *args, **kw): if self._active: - src,dst = self._remap_pair(name,src,dst,*args,**kw) - return original(src,dst,*args,**kw) + src, dst = self._remap_pair(name, src, dst, *args, **kw) + return original(src, dst, *args, **kw) return wrap for name in ["rename", "link", "symlink"]: - if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name) + if hasattr(_os, name): locals()[name] = _mk_dual_path_wrapper(name) def _mk_single_path_wrapper(name, original=None): - original = original or getattr(_os,name) + original = original or getattr(_os, name) - def wrap(self,path,*args,**kw): + def wrap(self, path, *args, **kw): if self._active: - path = self._remap_input(name,path,*args,**kw) - return original(path,*args,**kw) + path = self._remap_input(name, path, *args, **kw) + return original(path, *args, **kw) return wrap if _file: @@ -310,51 +310,51 @@ class AbstractSandbox: "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", "startfile", "mkfifo", "mknod", "pathconf", "access" ]: - if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name) + if hasattr(_os, name): locals()[name] = _mk_single_path_wrapper(name) def _mk_single_with_return(name): - original = getattr(_os,name) + original = getattr(_os, name) - def wrap(self,path,*args,**kw): + def wrap(self, path, *args, **kw): if self._active: - path = self._remap_input(name,path,*args,**kw) - return self._remap_output(name, original(path,*args,**kw)) - return original(path,*args,**kw) + path = self._remap_input(name, path, *args, **kw) + return self._remap_output(name, original(path, *args, **kw)) + return original(path, *args, **kw) return wrap for name in ['readlink', 'tempnam']: - if hasattr(_os,name): locals()[name] = _mk_single_with_return(name) + if hasattr(_os, name): locals()[name] = _mk_single_with_return(name) def _mk_query(name): - original = getattr(_os,name) + original = getattr(_os, name) - def wrap(self,*args,**kw): - retval = original(*args,**kw) + def wrap(self, *args, **kw): + retval = original(*args, **kw) if self._active: return self._remap_output(name, retval) return retval return wrap for name in ['getcwd', 'tmpnam']: - if hasattr(_os,name): locals()[name] = _mk_query(name) + if hasattr(_os, name): locals()[name] = _mk_query(name) - def _validate_path(self,path): + def _validate_path(self, path): """Called to remap or validate any path, whether input or output""" return path - def _remap_input(self,operation,path,*args,**kw): + def _remap_input(self, operation, path, *args, **kw): """Called for path inputs""" return self._validate_path(path) - def _remap_output(self,operation,path): + def _remap_output(self, operation, path): """Called for path outputs""" return self._validate_path(path) - def _remap_pair(self,operation,src,dst,*args,**kw): + def _remap_pair(self, operation, src, dst, *args, **kw): """Called for path pairs like rename, link, and symlink operations""" return ( - self._remap_input(operation+'-from',src,*args,**kw), - self._remap_input(operation+'-to',dst,*args,**kw) + self._remap_input(operation+'-from', src, *args, **kw), + self._remap_input(operation+'-to', dst, *args, **kw) ) @@ -388,7 +388,7 @@ class DirectorySandbox(AbstractSandbox): def __init__(self, sandbox, exceptions=_EXCEPTIONS): self._sandbox = os.path.normcase(os.path.realpath(sandbox)) - self._prefix = os.path.join(self._sandbox,'') + self._prefix = os.path.join(self._sandbox, '') self._exceptions = [ os.path.normcase(os.path.realpath(path)) for path in exceptions @@ -403,12 +403,12 @@ class DirectorySandbox(AbstractSandbox): def _file(self, path, mode='r', *args, **kw): if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): self._violation("file", path, mode, *args, **kw) - return _file(path,mode,*args,**kw) + return _file(path, mode, *args, **kw) def _open(self, path, mode='r', *args, **kw): if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): self._violation("open", path, mode, *args, **kw) - return _open(path,mode,*args,**kw) + return _open(path, mode, *args, **kw) def tmpnam(self): self._violation("tmpnam") @@ -448,13 +448,13 @@ class DirectorySandbox(AbstractSandbox): """Called for path pairs like rename, link, and symlink operations""" if not self._ok(src) or not self._ok(dst): self._violation(operation, src, dst, *args, **kw) - return (src,dst) + return (src, dst) def open(self, file, flags, mode=0o777, *args, **kw): """Called for low-level os.open()""" if flags & WRITE_FLAGS and not self._ok(file): self._violation("os.open", file, flags, mode, *args, **kw) - return _os.open(file,flags,mode, *args, **kw) + return _os.open(file, flags, mode, *args, **kw) WRITE_FLAGS = functools.reduce( operator.or_, [getattr(_os, a, 0) for a in diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py index c2168019..159d254e 100644 --- a/setuptools/site-patch.py +++ b/setuptools/site-patch.py @@ -7,7 +7,7 @@ def __boot(): else: PYTHONPATH = PYTHONPATH.split(os.pathsep) - pic = getattr(sys,'path_importer_cache',{}) + pic = getattr(sys, 'path_importer_cache', {}) stdpath = sys.path[len(PYTHONPATH):] mydir = os.path.dirname(__file__) #print "searching",stdpath,sys.path @@ -25,14 +25,14 @@ def __boot(): else: try: import imp # Avoid import loop in Python >= 3.3 - stream, path, descr = imp.find_module('site',[item]) + stream, path, descr = imp.find_module('site', [item]) except ImportError: continue if stream is None: continue try: # This should actually reload the current module - imp.load_module('site',stream,path,descr) + imp.load_module('site', stream, path, descr) finally: stream.close() break @@ -41,9 +41,9 @@ def __boot(): #print "loaded", __file__ - known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp + known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp - oldpos = getattr(sys,'__egginsert',0) # save old insertion position + oldpos = getattr(sys, '__egginsert', 0) # save old insertion position sys.__egginsert = 0 # and reset the current one for item in PYTHONPATH: diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 569b060f..50a4fff8 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -27,7 +27,7 @@ def makeSetup(**args): distutils.core._setup_stop_after = "commandline" # Don't let system command line leak into tests! - args.setdefault('script_args',['install']) + args.setdefault('script_args', ['install']) try: return setuptools.setup(**args) @@ -56,35 +56,35 @@ class TestDepends: fc = six.get_function_code(f1) # unrecognized name - assert dep.extract_constant(fc,'q', -1) is None + assert dep.extract_constant(fc, 'q', -1) is None # constant assigned - dep.extract_constant(fc,'x', -1) == "test" + dep.extract_constant(fc, 'x', -1) == "test" # expression assigned - dep.extract_constant(fc,'y', -1) == -1 + dep.extract_constant(fc, 'y', -1) == -1 # recognized name, not assigned - dep.extract_constant(fc,'z', -1) is None + dep.extract_constant(fc, 'z', -1) is None def testFindModule(self): with pytest.raises(ImportError): dep.find_module('no-such.-thing') with pytest.raises(ImportError): dep.find_module('setuptools.non-existent') - f,p,i = dep.find_module('setuptools.tests') + f, p, i = dep.find_module('setuptools.tests') f.close() @needs_bytecode def testModuleExtract(self): from email import __version__ - assert dep.get_module_constant('email','__version__') == __version__ - assert dep.get_module_constant('sys','version') == sys.version - assert dep.get_module_constant('setuptools.tests','__doc__') == __doc__ + assert dep.get_module_constant('email', '__version__') == __version__ + assert dep.get_module_constant('sys', 'version') == sys.version + assert dep.get_module_constant('setuptools.tests', '__doc__') == __doc__ @needs_bytecode def testRequire(self): - req = Require('Email','1.0.3','email') + req = Require('Email', '1.0.3', 'email') assert req.name == 'Email' assert req.module == 'email' @@ -101,12 +101,12 @@ class TestDepends: assert req.is_present() assert req.is_current() - req = Require('Email 3000','03000','email',format=LooseVersion) + req = Require('Email 3000', '03000', 'email', format=LooseVersion) assert req.is_present() assert not req.is_current() assert not req.version_ok('unknown') - req = Require('Do-what-I-mean','1.0','d-w-i-m') + req = Require('Do-what-I-mean', '1.0', 'd-w-i-m') assert not req.is_present() assert not req.is_current() @@ -125,22 +125,22 @@ class TestDepends: class TestDistro: def setup_method(self, method): - self.e1 = Extension('bar.ext',['bar.c']) + self.e1 = Extension('bar.ext', ['bar.c']) self.e2 = Extension('c.y', ['y.c']) self.dist = makeSetup( packages=['a', 'a.b', 'a.b.c', 'b', 'c'], - py_modules=['b.d','x'], + py_modules=['b.d', 'x'], ext_modules = (self.e1, self.e2), package_dir = {}, ) def testDistroType(self): - assert isinstance(self.dist,setuptools.dist.Distribution) + assert isinstance(self.dist, setuptools.dist.Distribution) def testExcludePackage(self): self.dist.exclude_package('a') - assert self.dist.packages == ['b','c'] + assert self.dist.packages == ['b', 'c'] self.dist.exclude_package('b') assert self.dist.packages == ['c'] @@ -169,7 +169,7 @@ class TestDistro: assert self.dist.ext_modules == [self.e2, self.e1] def testExcludePackages(self): - self.dist.exclude(packages=['c','b','a']) + self.dist.exclude(packages=['c', 'b', 'a']) assert self.dist.packages == [] assert self.dist.py_modules == ['x'] assert self.dist.ext_modules == [self.e1] @@ -199,13 +199,13 @@ class TestDistro: with pytest.raises(DistutilsSetupError): self.dist.exclude(nonexistent_option='x') with pytest.raises(DistutilsSetupError): - self.dist.include(packages={'x':'y'}) + self.dist.include(packages={'x': 'y'}) with pytest.raises(DistutilsSetupError): - self.dist.exclude(packages={'x':'y'}) + self.dist.exclude(packages={'x': 'y'}) with pytest.raises(DistutilsSetupError): - self.dist.include(ext_modules={'x':'y'}) + self.dist.include(ext_modules={'x': 'y'}) with pytest.raises(DistutilsSetupError): - self.dist.exclude(ext_modules={'x':'y'}) + self.dist.exclude(ext_modules={'x': 'y'}) with pytest.raises(DistutilsSetupError): self.dist.include(package_dir=['q']) @@ -216,31 +216,31 @@ class TestDistro: class TestFeatures: def setup_method(self, method): - self.req = Require('Distutils','1.0.3','distutils') + self.req = Require('Distutils', '1.0.3', 'distutils') self.dist = makeSetup( features={ - 'foo': Feature("foo",standard=True,require_features=['baz',self.req]), + 'foo': Feature("foo", standard=True, require_features=['baz', self.req]), 'bar': Feature("bar", standard=True, packages=['pkg.bar'], py_modules=['bar_et'], remove=['bar.ext'], ), 'baz': Feature( "baz", optional=False, packages=['pkg.baz'], scripts = ['scripts/baz_it'], - libraries=[('libfoo','foo/foofoo.c')] + libraries=[('libfoo', 'foo/foofoo.c')] ), 'dwim': Feature("DWIM", available=False, remove='bazish'), }, script_args=['--without-bar', 'install'], packages = ['pkg.bar', 'pkg.foo'], py_modules = ['bar_et', 'bazish'], - ext_modules = [Extension('bar.ext',['bar.c'])] + ext_modules = [Extension('bar.ext', ['bar.c'])] ) def testDefaults(self): assert not Feature( - "test",standard=True,remove='x',available=False + "test", standard=True, remove='x', available=False ).include_by_default() - assert Feature("test",standard=True,remove='x').include_by_default() + assert Feature("test", standard=True, remove='x').include_by_default() # Feature must have either kwargs, removes, or require_features with pytest.raises(DistutilsSetupError): Feature("test") @@ -252,16 +252,16 @@ class TestFeatures: def testFeatureOptions(self): dist = self.dist assert ( - ('with-dwim',None,'include DWIM') in dist.feature_options + ('with-dwim', None, 'include DWIM') in dist.feature_options ) assert ( - ('without-dwim',None,'exclude DWIM (default)') in dist.feature_options + ('without-dwim', None, 'exclude DWIM (default)') in dist.feature_options ) assert ( - ('with-bar',None,'include bar (default)') in dist.feature_options + ('with-bar', None, 'include bar (default)') in dist.feature_options ) assert ( - ('without-bar',None,'exclude bar') in dist.feature_options + ('without-bar', None, 'exclude bar') in dist.feature_options ) assert dist.feature_negopt['without-foo'] == 'with-foo' assert dist.feature_negopt['without-bar'] == 'with-bar' @@ -277,7 +277,7 @@ class TestFeatures: assert (not 'pkg.bar' in dist.packages) assert ('pkg.baz' in dist.packages) assert ('scripts/baz_it' in dist.scripts) - assert (('libfoo','foo/foofoo.c') in dist.libraries) + assert (('libfoo', 'foo/foofoo.c') in dist.libraries) assert dist.ext_modules == [] assert dist.require_features == [self.req] @@ -288,7 +288,7 @@ class TestFeatures: def testFeatureWithInvalidRemove(self): with pytest.raises(SystemExit): - makeSetup(features={'x':Feature('x', remove='y')}) + makeSetup(features={'x': Feature('x', remove='y')}) class TestCommandTests: @@ -298,7 +298,7 @@ class TestCommandTests: assert (isinstance(test_cmd, distutils.cmd.Command)) def testLongOptSuiteWNoDefault(self): - ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite']) + ts1 = makeSetup(script_args=['test', '--test-suite=foo.tests.suite']) ts1 = ts1.get_command_obj('test') ts1.ensure_finalized() assert ts1.test_suite == 'foo.tests.suite' @@ -311,7 +311,7 @@ class TestCommandTests: def testDefaultWModuleOnCmdLine(self): ts3 = makeSetup( test_suite='bar.tests', - script_args=['test','-m','foo.tests'] + script_args=['test', '-m', 'foo.tests'] ).get_command_obj('test') ts3.ensure_finalized() assert ts3.test_module == 'foo.tests' @@ -319,7 +319,7 @@ class TestCommandTests: def testConflictingOptions(self): ts4 = makeSetup( - script_args=['test','-m','bar.tests', '-s','foo.tests.suite'] + script_args=['test', '-m', 'bar.tests', '-s', 'foo.tests.suite'] ).get_command_obj('test') with pytest.raises(DistutilsOptionError): ts4.ensure_finalized() diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py index ae28c7c3..535ae107 100644 --- a/setuptools/tests/contexts.py +++ b/setuptools/tests/contexts.py @@ -10,7 +10,7 @@ import pkg_resources @contextlib.contextmanager -def tempdir(cd=lambda dir:None, **kwargs): +def tempdir(cd=lambda dir: None, **kwargs): temp_dir = tempfile.mkdtemp(**kwargs) orig_dir = os.getcwd() try: diff --git a/setuptools/tests/test_find_packages.py b/setuptools/tests/test_find_packages.py index 65e7243d..df51b04f 100644 --- a/setuptools/tests/test_find_packages.py +++ b/setuptools/tests/test_find_packages.py @@ -32,7 +32,7 @@ def can_symlink(): def has_symlink(): bad_symlink = ( # Windows symlink directory detection is broken on Python 3.2 - platform.system() == 'Windows' and sys.version_info[:2] == (3,2) + platform.system() == 'Windows' and sys.version_info[:2] == (3, 2) ) return can_symlink() and not bad_symlink -- cgit v1.2.1 From 39bcc16e47957bea4178a27f2ef872a78276ff40 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jul 2016 00:58:42 -0400 Subject: Remove commented code --- setuptools/site-patch.py | 3 --- setuptools/tests/test_build_py.py | 1 - 2 files changed, 4 deletions(-) diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py index 680e9355..3e89ef2f 100644 --- a/setuptools/site-patch.py +++ b/setuptools/site-patch.py @@ -10,7 +10,6 @@ def __boot(): pic = getattr(sys, 'path_importer_cache', {}) stdpath = sys.path[len(PYTHONPATH):] mydir = os.path.dirname(__file__) - # print "searching",stdpath,sys.path for item in stdpath: if item==mydir or not item: @@ -39,8 +38,6 @@ def __boot(): else: raise ImportError("Couldn't find the real 'site' module") - # print "loaded", __file__ - known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp oldpos = getattr(sys, '__egginsert', 0) # save old insertion position diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py index 860c569c..cc701ae6 100644 --- a/setuptools/tests/test_build_py.py +++ b/setuptools/tests/test_build_py.py @@ -26,6 +26,5 @@ def test_directories_in_package_data_glob(tmpdir_as_cwd): package_data={'': ['path/*']}, )) os.makedirs('path/subpath') - # with contexts.quiet(): dist.parse_command_line() dist.run_commands() -- cgit v1.2.1 From dc2d1dc249bec8e3a864e2aa6002a8e27adc4b7c Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 12:11:49 +0700 Subject: Fix missing whitespace around operator. --- pavement.py | 2 +- setuptools/depends.py | 20 +++++++-------- setuptools/dist.py | 48 +++++++++++++++++------------------ setuptools/lib2to3_ex.py | 2 +- setuptools/package_index.py | 40 ++++++++++++++--------------- setuptools/py31compat.py | 2 +- setuptools/sandbox.py | 6 ++--- setuptools/site-patch.py | 8 +++--- setuptools/ssl_support.py | 2 +- setuptools/tests/test_bdist_egg.py | 4 +-- setuptools/tests/test_easy_install.py | 4 +-- setuptools/tests/test_packageindex.py | 2 +- 12 files changed, 70 insertions(+), 70 deletions(-) diff --git a/pavement.py b/pavement.py index f620c790..3d840086 100644 --- a/pavement.py +++ b/pavement.py @@ -17,7 +17,7 @@ def update_vendored(): remove_all(vendor.glob('pyparsing*')) install_args = [ 'install', - '-r', str(vendor/'vendored.txt'), + '-r', str(vendor / 'vendored.txt'), '-t', str(vendor), ] pip.main(install_args) diff --git a/setuptools/depends.py b/setuptools/depends.py index eb1d7b13..01f4a23a 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -89,16 +89,16 @@ def _iter_code(code): ptr = 0 extended_arg = 0 - while ptr=HAVE_ARGUMENT: + if op >= HAVE_ARGUMENT: - arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg + arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg ptr += 3 - if op==EXTENDED_ARG: + if op == EXTENDED_ARG: long_type = six.integer_types[-1] extended_arg = arg * long_type(65536) continue @@ -119,7 +119,7 @@ def find_module(module, paths=None): part = parts.pop(0) f, path, (suffix, mode, kind) = info = imp.find_module(part, paths) - if kind==PKG_DIRECTORY: + if kind == PKG_DIRECTORY: parts = parts or ['__init__'] paths = [path] @@ -143,12 +143,12 @@ def get_module_constant(module, symbol, default=-1, paths=None): return None try: - if kind==PY_COMPILED: + if kind == PY_COMPILED: f.read(8) # skip magic & date code = marshal.load(f) - elif kind==PY_FROZEN: + elif kind == PY_FROZEN: code = imp.get_frozen_object(module) - elif kind==PY_SOURCE: + elif kind == PY_SOURCE: code = compile(f.read(), path, 'exec') else: # Not something we can parse; we'll have to import it. :( @@ -190,9 +190,9 @@ def extract_constant(code, symbol, default=-1): for op, arg in _iter_code(code): - if op==LOAD_CONST: + if op == LOAD_CONST: const = code.co_consts[arg] - elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL): + elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL): return const else: const = default diff --git a/setuptools/dist.py b/setuptools/dist.py index f229d726..b4ff3861 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -65,7 +65,7 @@ sequence = tuple, list def check_importable(dist, attr, value): try: - ep = pkg_resources.EntryPoint.parse('x='+value) + ep = pkg_resources.EntryPoint.parse('x=' + value) assert not ep.extras except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( @@ -77,7 +77,7 @@ def check_importable(dist, attr, value): def assert_string_list(dist, attr, value): """Verify that value is a string list or None""" try: - assert ''.join(value)!=value + assert ''.join(value) != value except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr, value) @@ -109,7 +109,7 @@ def check_extras(dist, attr, value): if ':' in k: k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): - raise DistutilsSetupError("Invalid environment marker: "+m) + raise DistutilsSetupError("Invalid environment marker: " + m) list(pkg_resources.parse_requirements(v)) except (TypeError, ValueError, AttributeError): raise DistutilsSetupError( @@ -162,7 +162,7 @@ def check_package_data(dist, attr, value): else: return raise DistutilsSetupError( - attr+" must be a dictionary mapping package names to lists of " + attr + " must be a dictionary mapping package names to lists of " "wildcard patterns" ) @@ -313,7 +313,7 @@ class Distribution(_Distribution): def _feature_attrname(self, name): """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-', '_') + return 'with_' + name.replace('-', '_') def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" @@ -402,13 +402,13 @@ class Distribution(_Distribution): if feature.optional: descr = feature.description incdef = ' (default)' - excdef='' + excdef = '' if not feature.include_by_default(): excdef, incdef = incdef, excdef - go.append(('with-'+name, None, 'include '+descr+incdef)) - go.append(('without-'+name, None, 'exclude '+descr+excdef)) - no['without-'+name] = 'with-'+name + go.append(('with-' + name, None, 'include ' + descr + incdef)) + go.append(('without-' + name, None, 'exclude ' + descr + excdef)) + no['without-' + name] = 'with-' + name self.global_options = self.feature_options = go + self.global_options self.negative_opt = self.feature_negopt = no @@ -469,7 +469,7 @@ class Distribution(_Distribution): def include_feature(self, name): """Request inclusion of feature named 'name'""" - if self.feature_is_included(name)==0: + if self.feature_is_included(name) == 0: descr = self.features[name].description raise DistutilsOptionError( descr + " is required, but was excluded or is not available" @@ -493,7 +493,7 @@ class Distribution(_Distribution): handle whatever special inclusion logic is needed. """ for k, v in attrs.items(): - include = getattr(self, '_include_'+k, None) + include = getattr(self, '_include_' + k, None) if include: include(v) else: @@ -502,7 +502,7 @@ class Distribution(_Distribution): def exclude_package(self, package): """Remove packages, modules, and extensions in named package""" - pfx = package+'.' + pfx = package + '.' if self.packages: self.packages = [ p for p in self.packages @@ -524,10 +524,10 @@ class Distribution(_Distribution): def has_contents_for(self, package): """Return true if 'exclude_package(package)' would do something""" - pfx = package+'.' + pfx = package + '.' for p in self.iter_distribution_names(): - if p==package or p.startswith(pfx): + if p == package or p.startswith(pfx): return True def _exclude_misc(self, name, value): @@ -544,7 +544,7 @@ class Distribution(_Distribution): ) if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( - name+": this setting cannot be changed via include/exclude" + name + ": this setting cannot be changed via include/exclude" ) elif old: setattr(self, name, [item for item in old if item not in value]) @@ -566,10 +566,10 @@ class Distribution(_Distribution): setattr(self, name, value) elif not isinstance(old, sequence): raise DistutilsSetupError( - name+": this setting cannot be changed via include/exclude" + name + ": this setting cannot be changed via include/exclude" ) else: - setattr(self, name, old+[item for item in value if item not in old]) + setattr(self, name, old + [item for item in value if item not in old]) def exclude(self, **attrs): """Remove items from distribution that are named in keyword arguments @@ -588,7 +588,7 @@ class Distribution(_Distribution): handle whatever special exclusion logic is needed. """ for k, v in attrs.items(): - exclude = getattr(self, '_exclude_'+k, None) + exclude = getattr(self, '_exclude_' + k, None) if exclude: exclude(v) else: @@ -648,19 +648,19 @@ class Distribution(_Distribution): opt = opt.replace('_', '-') - if val==0: + if val == 0: cmdobj = self.get_command_obj(cmd) neg_opt = self.negative_opt.copy() neg_opt.update(getattr(cmdobj, 'negative_opt', {})) for neg, pos in neg_opt.items(): - if pos==opt: - opt=neg - val=None + if pos == opt: + opt = neg + val = None break else: raise AssertionError("Shouldn't be able to get here") - elif val==1: + elif val == 1: val = None d.setdefault(cmd, {})[opt] = val @@ -835,7 +835,7 @@ class Feature: if not self.available: raise DistutilsPlatformError( - self.description+" is required, " + self.description + " is required, " "but is not available on this platform" ) diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index f7296786..ac5f8096 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -34,7 +34,7 @@ class Mixin2to3(_Mixin2to3): return if not files: return - log.info("Fixing "+" ".join(files)) + log.info("Fixing " + " ".join(files)) self.__build_fixer_names() self.__exclude_fixers() if doctests: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index cdedef83..4590e93f 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -80,7 +80,7 @@ def egg_info_for_url(url): parts = urllib.parse.urlparse(url) scheme, server, path, parameters, query, fragment = parts base = urllib.parse.unquote(path.split('/')[-1]) - if server=='sourceforge.net' and base=='download': # XXX Yuck + if server == 'sourceforge.net' and base == 'download': # XXX Yuck base = urllib.parse.unquote(path.split('/')[-2]) if '#' in base: base, fragment = base.split('#', 1) return base, fragment @@ -155,7 +155,7 @@ def interpret_distro_name( # it is a bdist_dumb, not an sdist -- bail out return - for p in range(1, len(parts)+1): + for p in range(1, len(parts) + 1): yield Distribution( location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), py_version=py_version, precedence = precedence, @@ -209,7 +209,7 @@ def find_external_links(url, page): for tag in ("Home Page", "Download URL"): pos = page.find(tag) - if pos!=-1: + if pos != -1: match = HREF.search(page, pos) if match: yield urllib.parse.urljoin(url, htmldecode(match.group(1))) @@ -336,7 +336,7 @@ class PackageIndex(Environment): for match in HREF.finditer(page): link = urllib.parse.urljoin(base, htmldecode(match.group(1))) self.process_url(link) - if url.startswith(self.index_url) and getattr(f, 'code', None)!=404: + if url.startswith(self.index_url) and getattr(f, 'code', None) != 404: page = self.process_index(url, page) def process_filename(self, fn, nested=False): @@ -357,7 +357,7 @@ class PackageIndex(Environment): def url_ok(self, url, fatal=False): s = URL_SCHEME(url) - if (s and s.group(1).lower()=='file') or self.allows(urllib.parse.urlparse(url)[1]): + if (s and s.group(1).lower() == 'file') or self.allows(urllib.parse.urlparse(url)[1]): return True msg = ("\nNote: Bypassing %s (disallowed host; see " "http://bit.ly/1dg9ijs for details).\n") @@ -400,7 +400,7 @@ class PackageIndex(Environment): parts = list(map( urllib.parse.unquote, link[len(self.index_url):].split('/') )) - if len(parts)==2 and '#' not in parts[1]: + if len(parts) == 2 and '#' not in parts[1]: # it's a package page, sanitize and index it pkg = safe_name(parts[0]) ver = safe_version(parts[1]) @@ -423,7 +423,7 @@ class PackageIndex(Environment): base, frag = egg_info_for_url(new_url) if base.endswith('.py') and not frag: if ver: - new_url+='#egg=%s-%s' % (pkg, ver) + new_url += '#egg=%s-%s' % (pkg, ver) else: self.need_version_info(url) self.scan_url(new_url) @@ -449,11 +449,11 @@ class PackageIndex(Environment): self.scan_url(self.index_url) def find_packages(self, requirement): - self.scan_url(self.index_url + requirement.unsafe_name+'/') + self.scan_url(self.index_url + requirement.unsafe_name + '/') if not self.package_pages.get(requirement.key): # Fall back to safe version of the name - self.scan_url(self.index_url + requirement.project_name+'/') + self.scan_url(self.index_url + requirement.project_name + '/') if not self.package_pages.get(requirement.key): # We couldn't find the target package, so search the index page too @@ -589,13 +589,13 @@ class PackageIndex(Environment): for dist in env[req.key]: - if dist.precedence==DEVELOP_DIST and not develop_ok: + if dist.precedence == DEVELOP_DIST and not develop_ok: if dist not in skipped: self.warn("Skipping development or system egg: %s", dist) skipped[dist] = 1 continue - if dist in req and (dist.precedence<=SOURCE_DIST or not source): + if dist in req and (dist.precedence <= SOURCE_DIST or not source): return dist if force_scan: @@ -645,7 +645,7 @@ class PackageIndex(Environment): interpret_distro_name(filename, match.group(1), None) if d.version ] or [] - if len(dists)==1: # unambiguous ``#egg`` fragment + if len(dists) == 1: # unambiguous ``#egg`` fragment basename = os.path.basename(filename) # Make sure the file has been downloaded to the temp dir. @@ -654,7 +654,7 @@ class PackageIndex(Environment): from setuptools.command.easy_install import samefile if not samefile(filename, dst): shutil.copy2(filename, dst) - filename=dst + filename = dst with open(os.path.join(tmpdir, 'setup.py'), 'w') as file: file.write( @@ -771,13 +771,13 @@ class PackageIndex(Environment): # Download the file # - if scheme=='svn' or scheme.startswith('svn+'): + if scheme == 'svn' or scheme.startswith('svn+'): return self._download_svn(url, filename) - elif scheme=='git' or scheme.startswith('git+'): + elif scheme == 'git' or scheme.startswith('git+'): return self._download_git(url, filename) elif scheme.startswith('hg+'): return self._download_hg(url, filename) - elif scheme=='file': + elif scheme == 'file': return urllib.request.url2pathname(urllib.parse.urlparse(url)[2]) else: self.url_ok(url, True) # raises error if not allowed @@ -806,7 +806,7 @@ class PackageIndex(Environment): break # not an index page file.close() os.unlink(filename) - raise DistutilsError("Unexpected HTML page found at "+url) + raise DistutilsError("Unexpected HTML page found at " + url) def _download_svn(self, url, filename): url = url.split('#', 1)[0] # remove any fragment for svn's sake @@ -821,7 +821,7 @@ class PackageIndex(Environment): user, pw = auth.split(':', 1) creds = " --username=%s --password=%s" % (user, pw) else: - creds = " --username="+auth + creds = " --username=" + auth netloc = host parts = scheme, netloc, url, p, q, f url = urllib.parse.urlunparse(parts) @@ -896,7 +896,7 @@ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub def uchr(c): if not isinstance(c, int): return c - if c>255: return six.unichr(c) + if c > 255: return six.unichr(c) return chr(c) @@ -1046,7 +1046,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): # Put authentication info back into request URL if same host, # so that links found on the page will work s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url) - if s2==scheme and h2==host: + if s2 == scheme and h2 == host: parts = s2, netloc, path2, param2, query2, frag2 fp.url = urllib.parse.urlunparse(parts) diff --git a/setuptools/py31compat.py b/setuptools/py31compat.py index 04a314ef..414b377b 100644 --- a/setuptools/py31compat.py +++ b/setuptools/py31compat.py @@ -12,7 +12,7 @@ except ImportError: def get_path(name): if name not in ('platlib', 'purelib'): raise ValueError("Name must be purelib or platlib") - return get_python_lib(name=='platlib') + return get_python_lib(name == 'platlib') try: # Python >=3.2 diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index c6bab096..5ed45f84 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -234,7 +234,7 @@ def run_setup(setup_script, args): setup_dir = os.path.abspath(os.path.dirname(setup_script)) with setup_context(setup_dir): try: - sys.argv[:] = [setup_script]+list(args) + sys.argv[:] = [setup_script] + list(args) sys.path.insert(0, setup_dir) # reset to include setup dir, w/clean callback list working_set.__init__() @@ -353,8 +353,8 @@ class AbstractSandbox: def _remap_pair(self, operation, src, dst, *args, **kw): """Called for path pairs like rename, link, and symlink operations""" return ( - self._remap_input(operation+'-from', src, *args, **kw), - self._remap_input(operation+'-to', dst, *args, **kw) + self._remap_input(operation + '-from', src, *args, **kw), + self._remap_input(operation + '-to', dst, *args, **kw) ) diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py index 3e89ef2f..6cfac4d6 100644 --- a/setuptools/site-patch.py +++ b/setuptools/site-patch.py @@ -2,7 +2,7 @@ def __boot(): import sys import os PYTHONPATH = os.environ.get('PYTHONPATH') - if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH): + if PYTHONPATH is None or (sys.platform == 'win32' and not PYTHONPATH): PYTHONPATH = [] else: PYTHONPATH = PYTHONPATH.split(os.pathsep) @@ -12,7 +12,7 @@ def __boot(): mydir = os.path.dirname(__file__) for item in stdpath: - if item==mydir or not item: + if item == mydir or not item: continue # skip if current dir. on Windows, or my own directory importer = pic.get(item) if importer is not None: @@ -55,7 +55,7 @@ def __boot(): for item in sys.path: p, np = makepath(item) - if np==nd and insert_at is None: + if np == nd and insert_at is None: # We've hit the first 'system' path entry, so added entries go here insert_at = len(new_path) @@ -68,6 +68,6 @@ def __boot(): sys.path[:] = new_path -if __name__=='site': +if __name__ == 'site': __boot() del __boot diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index ecede509..f4ba8a92 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -235,7 +235,7 @@ def get_win_certfile(): def find_ca_bundle(): """Return an existing CA bundle path, or None""" - if os.name=='nt': + if os.name == 'nt': return get_win_certfile() else: for cert_path in cert_paths: diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index a7ceac86..42c44edf 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -18,9 +18,9 @@ setup(name='foo', py_modules=['hi']) @pytest.yield_fixture def setup_context(tmpdir): - with (tmpdir/'setup.py').open('w') as f: + with (tmpdir / 'setup.py').open('w') as f: f.write(SETUP_PY) - with (tmpdir/'hi.py').open('w') as f: + with (tmpdir / 'hi.py').open('w') as f: f.write('1\n') with tmpdir.as_cwd(): yield tmpdir diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 894c4fd8..821e6fb2 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -157,7 +157,7 @@ class TestPTHFileWriter: @pytest.yield_fixture def setup_context(tmpdir): - with (tmpdir/'setup.py').open('w') as f: + with (tmpdir / 'setup.py').open('w') as f: f.write(SETUP_PY) with tmpdir.as_cwd(): yield tmpdir @@ -555,7 +555,7 @@ class TestScriptHeader: assert actual == expected actual = ei.ScriptWriter.get_script_header('#!/usr/bin/python', - executable='"'+self.exe_with_spaces+'"') + executable='"' + self.exe_with_spaces + '"') expected = '#!"%s"\n' % self.exe_with_spaces assert actual == expected diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 61f5909b..dda55382 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -129,7 +129,7 @@ class TestPackageIndex: # the distribution has been found assert 'foobar' in pi # we have only one link, because links are compared without md5 - assert len(pi['foobar'])==1 + assert len(pi['foobar']) == 1 # the link should be from the index assert 'correct_md5' in pi['foobar'][0].location -- cgit v1.2.1 From a0d8df6b769dd6d83dd88d9f4f9aa6234b9bcbc2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jul 2016 01:12:04 -0400 Subject: Update changelog --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e04bdf05..925c05be 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v24.0.3 +------- + +* Updated style in much of the codebase to match + community expectations. See #632, #633, #634, + #637, #639, #638, #642, #648. + v24.0.2 ------- -- cgit v1.2.1 From c24033af6c965481b34296180d7daef749daf179 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jul 2016 01:12:14 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.0.2=20=E2=86=92=2024.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 303569f6..5ded051d 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.0.2 +current_version = 24.0.3 commit = True tag = True diff --git a/setup.py b/setup.py index bac726bf..1d742add 100755 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.0.2", + version="24.0.3", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 8e80ee745b620086bea2111236f85fa2376d748f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jul 2016 01:12:15 -0400 Subject: Added tag v24.0.3 for changeset d425bd1ee620 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index afb158b1..17e1c0a4 100644 --- a/.hgtags +++ b/.hgtags @@ -280,3 +280,4 @@ a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 8d37b17a93ec3e5fff9e040fc3f14ab7b7b24b2c v24.0.0 130a58f9503fe07ca8c7a34675b7d3a976f163d7 v24.0.1 7996c56bf6a2f81427b2f91eb11e64d690353493 v24.0.2 +d425bd1ee620772fe90e0dd2a7530b0d6a642601 v24.0.3 -- cgit v1.2.1 From 93612718350a0dd0bb09d21986ef9333a6f5cb1f Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 12:32:22 +0700 Subject: Fix spacing after comment hash. --- setuptools/package_index.py | 2 +- setuptools/py31compat.py | 4 ++-- setuptools/site-patch.py | 4 ++-- setuptools/tests/test_dist_info.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index cdedef83..4606c5ae 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -325,7 +325,7 @@ class PackageIndex(Environment): base = f.url # handle redirects page = f.read() - if not isinstance(page, str): # We are in Python 3 and got bytes. We want str. + if not isinstance(page, str): # We are in Python 3 and got bytes. We want str. if isinstance(f, urllib.error.HTTPError): # Errors have no charset, assume latin1: charset = 'latin-1' diff --git a/setuptools/py31compat.py b/setuptools/py31compat.py index 04a314ef..c2c4eed8 100644 --- a/setuptools/py31compat.py +++ b/setuptools/py31compat.py @@ -29,7 +29,7 @@ except ImportError: """ def __init__(self): - self.name = None # Handle mkdtemp raising an exception + self.name = None # Handle mkdtemp raising an exception self.name = tempfile.mkdtemp() def __enter__(self): @@ -38,7 +38,7 @@ except ImportError: def __exit__(self, exctype, excvalue, exctrace): try: shutil.rmtree(self.name, True) - except OSError: #removal errors are not the only possible + except OSError: # removal errors are not the only possible pass self.name = None diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py index 3e89ef2f..9af7c690 100644 --- a/setuptools/site-patch.py +++ b/setuptools/site-patch.py @@ -23,7 +23,7 @@ def __boot(): break else: try: - import imp # Avoid import loop in Python >= 3.3 + import imp # Avoid import loop in Python >= 3.3 stream, path, descr = imp.find_module('site', [item]) except ImportError: continue @@ -38,7 +38,7 @@ def __boot(): else: raise ImportError("Couldn't find the real 'site' module") - known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp + known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp oldpos = getattr(sys, '__egginsert', 0) # save old insertion position sys.__egginsert = 0 # and reset the current one diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 9f226a55..db3cb2e7 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -25,8 +25,8 @@ class TestDistInfo: unversioned = dists['UnversionedDistribution'] versioned = dists['VersionedDistribution'] - assert versioned.version == '2.718' # from filename - assert unversioned.version == '0.3' # from METADATA + assert versioned.version == '2.718' # from filename + assert unversioned.version == '0.3' # from METADATA def test_conditional_dependencies(self): specs = 'splort==4', 'quux>=1.1' -- cgit v1.2.1 From c72c5966e23de51fc7ab460faad5a6d3d20531c9 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 12:37:39 +0700 Subject: Make exactly one space after comma. --- setuptools/tests/__init__.py | 2 +- setuptools/tests/environment.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 91519682..ad7d7ee5 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -220,7 +220,7 @@ class TestFeatures: self.dist = makeSetup( features={ 'foo': Feature("foo", standard=True, require_features=['baz', self.req]), - 'bar': Feature("bar", standard=True, packages=['pkg.bar'], + 'bar': Feature("bar", standard=True, packages=['pkg.bar'], py_modules=['bar_et'], remove=['bar.ext'], ), 'baz': Feature( diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py index e5237721..b0e3bd36 100644 --- a/setuptools/tests/environment.py +++ b/setuptools/tests/environment.py @@ -51,7 +51,7 @@ def run_setup_py(cmd, pypath=None, path=None, return 1, '' # decode the console string if needed - if hasattr(data, "decode"): + if hasattr(data, "decode"): # use the default encoding data = data.decode() data = unicodedata.normalize('NFC', data) -- cgit v1.2.1 From a4190dd14802db1017f67332919377c9e14f95ad Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 08:58:30 +0700 Subject: Match closing bracket identation of opening bracket's line. --- setuptools/tests/test_bdist_egg.py | 2 +- setuptools/tests/test_egg_info.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index a7ceac86..4cbfdb64 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -33,7 +33,7 @@ class Test: script_args=['bdist_egg'], name='foo', py_modules=['hi'] - )) + )) os.makedirs(os.path.join('build', 'src')) with contexts.quiet(): dist.parse_command_line() diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index afbda2cc..dea7f992 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -179,7 +179,7 @@ class TestEggInfo(object): """ % requires_line) build_files({ 'setup.py': setup_script, - }) + }) def test_install_requires_with_markers(self, tmpdir_cwd, env): self._setup_script_with_requires( -- cgit v1.2.1 From 2e1ca9656b566b57c7736ef229e9d46c0aa7216c Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 12:47:40 +0700 Subject: Remove whitespace around parameter '=' sign. --- setuptools/launch.py | 6 +++--- setuptools/lib2to3_ex.py | 2 +- setuptools/package_index.py | 6 +++--- setuptools/tests/__init__.py | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/setuptools/launch.py b/setuptools/launch.py index b05cbd2c..308283ea 100644 --- a/setuptools/launch.py +++ b/setuptools/launch.py @@ -18,9 +18,9 @@ def run(): __builtins__ script_name = sys.argv[1] namespace = dict( - __file__ = script_name, - __name__ = '__main__', - __doc__ = None, + __file__=script_name, + __name__='__main__', + __doc__=None, ) sys.argv[:] = sys.argv[1:] diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index f7296786..308086bc 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -27,7 +27,7 @@ class DistutilsRefactoringTool(RefactoringTool): class Mixin2to3(_Mixin2to3): - def run_2to3(self, files, doctests = False): + def run_2to3(self, files, doctests=False): # See of the distribution option has been set, otherwise check the # setuptools default. if self.distribution.use_2to3 is not True: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index cdedef83..45ea49ad 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -94,7 +94,7 @@ def distros_for_url(url, metadata=None): match = EGG_FRAGMENT.match(fragment) if match: for dist in interpret_distro_name( - url, match.group(1), metadata, precedence = CHECKOUT_DIST + url, match.group(1), metadata, precedence=CHECKOUT_DIST ): yield dist @@ -158,8 +158,8 @@ def interpret_distro_name( for p in range(1, len(parts)+1): yield Distribution( location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), - py_version=py_version, precedence = precedence, - platform = platform + py_version=py_version, precedence=precedence, + platform=platform ) diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 91519682..c059b178 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -131,8 +131,8 @@ class TestDistro: self.dist = makeSetup( packages=['a', 'a.b', 'a.b.c', 'b', 'c'], py_modules=['b.d', 'x'], - ext_modules = (self.e1, self.e2), - package_dir = {}, + ext_modules=(self.e1, self.e2), + package_dir={}, ) def testDistroType(self): @@ -225,15 +225,15 @@ class TestFeatures: ), 'baz': Feature( "baz", optional=False, packages=['pkg.baz'], - scripts = ['scripts/baz_it'], + scripts=['scripts/baz_it'], libraries=[('libfoo', 'foo/foofoo.c')] ), 'dwim': Feature("DWIM", available=False, remove='bazish'), }, script_args=['--without-bar', 'install'], - packages = ['pkg.bar', 'pkg.foo'], - py_modules = ['bar_et', 'bazish'], - ext_modules = [Extension('bar.ext', ['bar.c'])] + packages=['pkg.bar', 'pkg.foo'], + py_modules=['bar_et', 'bazish'], + ext_modules=[Extension('bar.ext', ['bar.c'])] ) def testDefaults(self): -- cgit v1.2.1 From 98477464ed0d9ffd0b39e8baac15dfcd568dcb22 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Fri, 8 Jul 2016 20:39:10 +0200 Subject: Add python/external_requires keywords to setup This should allow setuptools to write the metadata Requires-Python and Requires-External from PEP345 to the PKGINFO file --- setup.py | 2 ++ setuptools/dist.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/setup.py b/setup.py index 1d742add..1960f653 100755 --- a/setup.py +++ b/setup.py @@ -95,6 +95,8 @@ setup_params = dict( "install_requires = setuptools.dist:check_requirements", "tests_require = setuptools.dist:check_requirements", "setup_requires = setuptools.dist:check_requirements", + "python_requires = setuptools.dist:check_specifier", + "external_requires = setuptools.dist:assert_string_list", "entry_points = setuptools.dist:check_entry_points", "test_suite = setuptools.dist:check_test_suite", "zip_safe = setuptools.dist:assert_bool", diff --git a/setuptools/dist.py b/setuptools/dist.py index f229d726..29b0f266 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -39,6 +39,22 @@ def _get_unpatched(cls): _Distribution = _get_unpatched(_Distribution) +def _patch_distribution_metadata_write_pkg_file(): + """Patch write_pkg_file to also write Requires-Python/Requires-External""" + original_write = distutils.dist.DistributionMetadata.write_pkg_file + def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. + """ + original_write(self, file) + if hasattr(self, 'python_requires'): + file.write('Requires-Python: %s\n' % self.python_requires) + if getattr(self, 'external_requires', []): + self._write_list(file, 'Requires-External', self.external_requires) + + distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file +_patch_distribution_metadata_write_pkg_file() + + def _patch_distribution_metadata_write_pkg_info(): """ Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local @@ -138,6 +154,18 @@ def check_requirements(dist, attr, value): raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) +def check_specifier(dist, attr, value): + """Verify that value is a valid version specifier""" + try: + packaging.specifiers.SpecifierSet(value) + except packaging.specifiers.InvalidSpecifier as error: + tmpl = ( + "{attr!r} must be a string or list of strings " + "containing valid version specifiers; {error}" + ) + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + + def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: @@ -303,6 +331,10 @@ class Distribution(_Distribution): "setuptools, pip, and PyPI. Please see PEP 440 for more " "details." % self.metadata.version ) + if getattr(self, 'python_requires', None): + self.metadata.python_requires = self.python_requires + if getattr(self, 'external_requires', None): + self.metadata.external_requires = self.external_requires def parse_command_line(self): """Process features after parsing command line options""" -- cgit v1.2.1 From d9a251b704b87e9a0e5090213e7799c468c4bbf5 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Sun, 10 Jul 2016 20:51:37 +0200 Subject: Drop external_requires keyword --- setup.py | 1 - setuptools/dist.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/setup.py b/setup.py index 1960f653..0a031b05 100755 --- a/setup.py +++ b/setup.py @@ -96,7 +96,6 @@ setup_params = dict( "tests_require = setuptools.dist:check_requirements", "setup_requires = setuptools.dist:check_requirements", "python_requires = setuptools.dist:check_specifier", - "external_requires = setuptools.dist:assert_string_list", "entry_points = setuptools.dist:check_entry_points", "test_suite = setuptools.dist:check_test_suite", "zip_safe = setuptools.dist:assert_bool", diff --git a/setuptools/dist.py b/setuptools/dist.py index 29b0f266..27cf8501 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -48,8 +48,6 @@ def _patch_distribution_metadata_write_pkg_file(): original_write(self, file) if hasattr(self, 'python_requires'): file.write('Requires-Python: %s\n' % self.python_requires) - if getattr(self, 'external_requires', []): - self._write_list(file, 'Requires-External', self.external_requires) distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file _patch_distribution_metadata_write_pkg_file() @@ -333,8 +331,6 @@ class Distribution(_Distribution): ) if getattr(self, 'python_requires', None): self.metadata.python_requires = self.python_requires - if getattr(self, 'external_requires', None): - self.metadata.external_requires = self.external_requires def parse_command_line(self): """Process features after parsing command line options""" -- cgit v1.2.1 From 020771f5e631741de31255283aa81adc05a26a9d Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Mon, 11 Jul 2016 11:30:11 +0200 Subject: Add basic tests and docs for python_requires --- docs/setuptools.txt | 4 ++++ setuptools/tests/test_egg_info.py | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 807a2722..0f955663 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -302,6 +302,10 @@ unless you need the associated ``setuptools`` feature. installed to support those features. See the section below on `Declaring Dependencies`_ for details and examples of the format of this argument. +``python_requires`` + A string corresponding to a version specifier (as defined in PEP 440) for + the Python version, used to specify the Requires-Python defined in PEP 345. + ``setup_requires`` A string or list of strings specifying what other distributions need to be present in order for the *setup script* to run. ``setuptools`` will diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index afbda2cc..d758ff90 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -210,6 +210,30 @@ class TestEggInfo(object): self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_python_requires_egg_info(self, tmpdir_cwd, env): + self._setup_script_with_requires( + """python_requires='>=2.7.12',""") + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + pkginfo = os.path.join(egg_info_dir, 'PKG-INFO') + assert 'Requires-Python: >=2.7.12' in open(pkginfo).read().split('\n') + + def test_python_requires_install(self, tmpdir_cwd, env): + self._setup_script_with_requires( + """python_requires='>=1.2.3',""") + self._run_install_command(tmpdir_cwd, env) + egg_info_dir = self._find_egg_info_files(env.paths['lib']).base + pkginfo = os.path.join(egg_info_dir, 'PKG-INFO') + assert 'Requires-Python: >=1.2.3' in open(pkginfo).read().split('\n') + def _run_install_command(self, tmpdir_cwd, env, cmd=None, output=None): environ = os.environ.copy().update( HOME=env.paths['home'], -- cgit v1.2.1 From 01de794bc829cc9eb0c1512b3570acec970e1acf Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 21:45:22 +0700 Subject: Put imports in same block alphabeticaly. --- setuptools/archive_util.py | 2 +- setuptools/command/bdist_egg.py | 2 +- setuptools/depends.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- setuptools/tests/test_packageindex.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index d1950638..3b41db15 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -12,8 +12,8 @@ import os import shutil import posixpath import contextlib -from pkg_resources import ensure_directory, ContextualZipFile from distutils.errors import DistutilsError +from pkg_resources import ensure_directory, ContextualZipFile class UnrecognizedFormat(DistutilsError): diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 9cebd7fa..c40022a1 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -8,8 +8,8 @@ from distutils import log from types import CodeType import sys import os -import marshal import textwrap +import marshal from setuptools.extern import six diff --git a/setuptools/depends.py b/setuptools/depends.py index eb1d7b13..865d4151 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -1,8 +1,8 @@ import sys import imp import marshal -from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN from distutils.version import StrictVersion +from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN from setuptools.extern import six diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 894c4fd8..94b25f2e 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -15,8 +15,8 @@ import itertools import distutils.errors import io -from setuptools.extern.six.moves import urllib import time +from setuptools.extern.six.moves import urllib import pytest try: diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 61f5909b..877f3049 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -7,10 +7,10 @@ import distutils.errors from setuptools.extern import six from setuptools.extern.six.moves import urllib, http_client -from .textwrap import DALS import pkg_resources import setuptools.package_index from setuptools.tests.server import IndexServer +from .textwrap import DALS class TestPackageIndex: -- cgit v1.2.1 From c07d0df02c9a22838099e1d86f31015d19c92a87 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 21:52:34 +0700 Subject: Use isinstance() instead of type() for a typecheck. --- setuptools/command/upload_docs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 01b49046..ccc1c76f 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -105,7 +105,7 @@ class upload_docs(upload): if not isinstance(values, list): values = [values] for value in values: - if type(value) is tuple: + if isinstance(value, tuple): title += '; filename="%s"' % value[0] value = value[1] else: -- cgit v1.2.1 From 80a2ff9e3055e216a5238e81a6933f6a342b17fb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 11:10:01 -0400 Subject: Add safeguard against improper installs when using a clean repository checkout. Fixes #659. --- CHANGES.rst | 6 ++++++ setup.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 925c05be..aa1d1b83 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v24.1.0 +------- + +* #659: ``setup.py`` now will fail fast and with a helpful + error message when the necessary metadata is missing. + v24.0.3 ------- diff --git a/setup.py b/setup.py index 1d742add..69c55417 100755 --- a/setup.py +++ b/setup.py @@ -11,6 +11,11 @@ import textwrap # Allow to run setup.py from another directory. os.chdir(os.path.dirname(os.path.abspath(__file__))) +# Prevent improper installs without necessary metadata. See #659 +if not os.path.exists('setuptools.egg-info'): + msg = "Cannot build setuptools without metadata. Run bootstrap.py" + raise RuntimeError(msg) + src_root = None from distutils.util import convert_path -- cgit v1.2.1 From 39374cb5747b8cc18240f474d10d17913f203b67 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 11:43:04 -0400 Subject: Add test capturing expectation of PYPI_MD5 pattern --- setuptools/tests/test_packageindex.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index dda55382..b2b0d537 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -15,6 +15,16 @@ from setuptools.tests.server import IndexServer class TestPackageIndex: + def test_regex(self): + hash_url = 'http://other_url?:action=show_md5&' + hash_url += 'digest=0123456789abcdef0123456789abcdef' + doc = """ + Name + (md5) + """.lstrip().format(**locals()) + assert setuptools.package_index.PYPI_MD5.match(doc) + def test_bad_url_bad_port(self): index = setuptools.package_index.PackageIndex() url = 'http://127.0.0.1:0/nonesuch/test_package_index' -- cgit v1.2.1 From 9879ab252e02c3c482536e31067a567df223dd28 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 11:55:29 -0400 Subject: Update changelog --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index aa1d1b83..f28164d6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,8 @@ v24.1.0 * #659: ``setup.py`` now will fail fast and with a helpful error message when the necessary metadata is missing. +* More style updates. See #656, #635, #640, + #644, #650, #652, and #655. v24.0.3 ------- -- cgit v1.2.1 From ad2b148e4c07c7d0195d4265d6bb144ef6465065 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 11:55:34 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.0.3=20=E2=86=92=2024.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 5ded051d..2e0cc267 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.0.3 +current_version = 24.1.0 commit = True tag = True diff --git a/setup.py b/setup.py index 69c55417..48a20b81 100755 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.0.3", + version="24.1.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From e6d6f7bd99d7329a5689980ac74c9e227bd0f503 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 11:55:35 -0400 Subject: Added tag v24.1.0 for changeset a7d2f79f0996 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 17e1c0a4..812b7a3f 100644 --- a/.hgtags +++ b/.hgtags @@ -281,3 +281,4 @@ a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 130a58f9503fe07ca8c7a34675b7d3a976f163d7 v24.0.1 7996c56bf6a2f81427b2f91eb11e64d690353493 v24.0.2 d425bd1ee620772fe90e0dd2a7530b0d6a642601 v24.0.3 +a7d2f79f0996d881794af0f87595032098202811 v24.1.0 -- cgit v1.2.1 From 0ef2f3616fd1e19ef2fafeebbc7f904a1fefcd51 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 04:07:20 +0700 Subject: Test for membership should be 'not in'. --- setuptools/tests/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 66f23854..53bd836c 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -266,15 +266,15 @@ class TestFeatures: assert dist.feature_negopt['without-foo'] == 'with-foo' assert dist.feature_negopt['without-bar'] == 'with-bar' assert dist.feature_negopt['without-dwim'] == 'with-dwim' - assert (not 'without-baz' in dist.feature_negopt) + assert ('without-baz' not in dist.feature_negopt) def testUseFeatures(self): dist = self.dist assert dist.with_foo == 1 assert dist.with_bar == 0 assert dist.with_baz == 1 - assert (not 'bar_et' in dist.py_modules) - assert (not 'pkg.bar' in dist.packages) + assert ('bar_et' not in dist.py_modules) + assert ('pkg.bar' not in dist.packages) assert ('pkg.baz' in dist.packages) assert ('scripts/baz_it' in dist.scripts) assert (('libfoo', 'foo/foofoo.c') in dist.libraries) -- cgit v1.2.1 From f2bf9162c26ee17cef89425135d2c684d6e8ce98 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 04:25:53 +0700 Subject: Format block comment. --- setuptools/tests/test_develop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index d22e5e4a..f1580785 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -114,4 +114,4 @@ class TestDevelop: cmd.ensure_finalized() cmd.install_dir = tmpdir cmd.run() - #assert '0.0' not in foocmd_text + # assert '0.0' not in foocmd_text -- cgit v1.2.1 From 64335b63f9e03e71d0acd885b8bfd0b4b7a60aa8 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 04:13:28 +0700 Subject: Put colon-separated compound statement on separate lines. --- pkg_resources/__init__.py | 3 ++- setuptools/depends.py | 3 ++- setuptools/dist.py | 9 ++++++--- setuptools/lib2to3_ex.py | 3 ++- setuptools/package_index.py | 21 ++++++++++++++------- setuptools/sandbox.py | 12 ++++++++---- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 15cb93fd..6337db99 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2837,7 +2837,8 @@ class Requirement(packaging.requirements.Requirement): def _get_mro(cls): """Get an mro for a type or classic class""" if not isinstance(cls, type): - class cls(cls, object): pass + class cls(cls, object): + pass return cls.__mro__[1:] return cls.__mro__ diff --git a/setuptools/depends.py b/setuptools/depends.py index 48c20156..75344590 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -53,7 +53,8 @@ class Require: if self.attribute is None: try: f, p, i = find_module(self.module, paths) - if f: f.close() + if f: + f.close() return default except ImportError: return None diff --git a/setuptools/dist.py b/setuptools/dist.py index b4ff3861..b8ada9b9 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -155,8 +155,10 @@ def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" if isinstance(value, dict): for k, v in value.items(): - if not isinstance(k, str): break - try: iter(v) + if not isinstance(k, str): + break + try: + iter(v) except TypeError: break else: @@ -807,7 +809,8 @@ class Feature: r for r in require_features if isinstance(r, str) ] er = [r for r in require_features if not isinstance(r, str)] - if er: extras['require_features'] = er + if er: + extras['require_features'] = er if isinstance(remove, str): remove = remove, diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index c58d8407..467e57d2 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -45,7 +45,8 @@ class Mixin2to3(_Mixin2to3): _Mixin2to3.run_2to3(self, files) def __build_fixer_names(self): - if self.fixer_names: return + if self.fixer_names: + return self.fixer_names = [] for p in setuptools.lib2to3_fixer_packages: self.fixer_names.extend(get_fixers_from_package(p)) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 8764faa6..77f5f96e 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -82,14 +82,16 @@ def egg_info_for_url(url): base = urllib.parse.unquote(path.split('/')[-1]) if server == 'sourceforge.net' and base == 'download': # XXX Yuck base = urllib.parse.unquote(path.split('/')[-2]) - if '#' in base: base, fragment = base.split('#', 1) + if '#' in base: + base, fragment = base.split('#', 1) return base, fragment def distros_for_url(url, metadata=None): """Yield egg or source distribution objects that might be found at a URL""" base, fragment = egg_info_for_url(url) - for dist in distros_for_location(url, base, metadata): yield dist + for dist in distros_for_location(url, base, metadata): + yield dist if fragment: match = EGG_FRAGMENT.match(fragment) if match: @@ -289,7 +291,8 @@ class PackageIndex(Environment): self.to_scan = [] if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()): self.opener = ssl_support.opener_for(ca_bundle) - else: self.opener = urllib.request.urlopen + else: + self.opener = urllib.request.urlopen def process_url(self, url, retrieve=False): """Evaluate a URL as a possible download, and maybe retrieve it""" @@ -317,7 +320,8 @@ class PackageIndex(Environment): self.info("Reading %s", url) self.fetched_urls[url] = True # prevent multiple fetch attempts f = self.open_url(url, "Download error on %s: %%s -- Some packages may not be found!" % url) - if f is None: return + if f is None: + return self.fetched_urls[f.url] = True if 'html' not in f.headers.get('content-type', '').lower(): f.close() # not html, we can't process it @@ -442,7 +446,8 @@ class PackageIndex(Environment): def scan_all(self, msg=None, *args): if self.index_url not in self.fetched_urls: - if msg: self.warn(msg, *args) + if msg: + self.warn(msg, *args) self.info( "Scanning index of all packages (this may take a while)" ) @@ -714,7 +719,8 @@ class PackageIndex(Environment): self.check_hash(checker, filename, tfp) return headers finally: - if fp: fp.close() + if fp: + fp.close() def reporthook(self, url, filename, blocknum, blksize, size): pass # no-op @@ -896,7 +902,8 @@ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub def uchr(c): if not isinstance(c, int): return c - if c > 255: return six.unichr(c) + if c > 255: + return six.unichr(c) return chr(c) diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 5ed45f84..c89593b1 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -291,7 +291,8 @@ class AbstractSandbox: return wrap for name in ["rename", "link", "symlink"]: - if hasattr(_os, name): locals()[name] = _mk_dual_path_wrapper(name) + if hasattr(_os, name): + locals()[name] = _mk_dual_path_wrapper(name) def _mk_single_path_wrapper(name, original=None): original = original or getattr(_os, name) @@ -310,7 +311,8 @@ class AbstractSandbox: "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", "startfile", "mkfifo", "mknod", "pathconf", "access" ]: - if hasattr(_os, name): locals()[name] = _mk_single_path_wrapper(name) + if hasattr(_os, name): + locals()[name] = _mk_single_path_wrapper(name) def _mk_single_with_return(name): original = getattr(_os, name) @@ -323,7 +325,8 @@ class AbstractSandbox: return wrap for name in ['readlink', 'tempnam']: - if hasattr(_os, name): locals()[name] = _mk_single_with_return(name) + if hasattr(_os, name): + locals()[name] = _mk_single_with_return(name) def _mk_query(name): original = getattr(_os, name) @@ -336,7 +339,8 @@ class AbstractSandbox: return wrap for name in ['getcwd', 'tmpnam']: - if hasattr(_os, name): locals()[name] = _mk_query(name) + if hasattr(_os, name): + locals()[name] = _mk_query(name) def _validate_path(self, path): """Called to remap or validate any path, whether input or output""" -- cgit v1.2.1 From 82b908759cddf344274f8c9360eaef79517e9c7f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 19:26:20 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f28164d6..2946fc6d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v24.1.1 +------- + +* More style updates. See #660, #661, #641. + v24.1.0 ------- -- cgit v1.2.1 From 01463e00ca98f74f1e0d5923a0d16c635be4f94c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 19:27:44 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.1.0=20=E2=86=92=2024.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 2e0cc267..87265c01 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.1.0 +current_version = 24.1.1 commit = True tag = True diff --git a/setup.py b/setup.py index 48a20b81..2de0a135 100755 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.1.0", + version="24.1.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 18f760aa07da104d7ed156b7f085bd05fb0c964d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 19:27:44 -0400 Subject: Added tag v24.1.1 for changeset d29075e7f879 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 812b7a3f..5cc35326 100644 --- a/.hgtags +++ b/.hgtags @@ -282,3 +282,4 @@ a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 7996c56bf6a2f81427b2f91eb11e64d690353493 v24.0.2 d425bd1ee620772fe90e0dd2a7530b0d6a642601 v24.0.3 a7d2f79f0996d881794af0f87595032098202811 v24.1.0 +d29075e7f8797891e8c59fb58c4d8d1b79954b34 v24.1.1 -- cgit v1.2.1 From b4e1b1f67d9cd50086c7acafcdff8c02b41988b3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 19:30:22 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2946fc6d..28924a97 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v24.2.0 +------- + +* #631: Add support for ``python_requires`` keyword. + v24.1.1 ------- -- cgit v1.2.1 From 587fef5c66600232a290ce71c2e1ddf2727d3b41 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 19:30:28 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.1.1=20=E2=86=92=2024.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 87265c01..f94f03c8 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.1.1 +current_version = 24.2.0 commit = True tag = True diff --git a/setup.py b/setup.py index 67407c74..e9aa97a9 100755 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.1.1", + version="24.2.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 966e2fa4118277ed4551aa2215ac6ebac34b37e5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 20 Jul 2016 19:30:28 -0400 Subject: Added tag v24.2.0 for changeset ed9e7bd8caf9 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 5cc35326..85c47bdb 100644 --- a/.hgtags +++ b/.hgtags @@ -283,3 +283,4 @@ a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 d425bd1ee620772fe90e0dd2a7530b0d6a642601 v24.0.3 a7d2f79f0996d881794af0f87595032098202811 v24.1.0 d29075e7f8797891e8c59fb58c4d8d1b79954b34 v24.1.1 +ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 -- cgit v1.2.1 From 39bf3155d47c0024240be414a611dcb6d549f53c Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 09:37:34 +0700 Subject: Add missing blank lines after class or function definition. --- setup.py | 1 + setuptools/__init__.py | 2 ++ setuptools/archive_util.py | 1 + setuptools/command/bdist_egg.py | 1 + setuptools/command/easy_install.py | 2 ++ setuptools/depends.py | 1 + setuptools/dist.py | 6 ++++++ setuptools/extension.py | 2 ++ setuptools/package_index.py | 3 +++ setuptools/py26compat.py | 1 + setuptools/py27compat.py | 1 + setuptools/sandbox.py | 1 + setuptools/site-patch.py | 1 + setuptools/tests/py26compat.py | 1 + setuptools/tests/test_easy_install.py | 1 + tests/manual_test.py | 2 ++ 16 files changed, 27 insertions(+) diff --git a/setup.py b/setup.py index e9aa97a9..7bd3a079 100755 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ def _gen_console_scripts(): yield ("easy_install-{shortver} = setuptools.command.easy_install:main" .format(shortver=sys.version[:3])) + console_scripts = list(_gen_console_scripts()) readme_file = io.open('README.rst', encoding='utf-8') diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 2523ccc7..cf0c39f2 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -116,6 +116,7 @@ class PEP420PackageFinder(PackageFinder): def _looks_like_package(path): return True + find_packages = PackageFinder.find setup = distutils.core.setup @@ -141,6 +142,7 @@ class Command(_Command): vars(cmd).update(kw) return cmd + # we can't patch distutils.cmd, alas distutils.core.Command = Command diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index 3b41db15..24d4e7bb 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -169,4 +169,5 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): pass return True + extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index c40022a1..50744b87 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -432,6 +432,7 @@ def can_scan(): # Attribute names of options for commands that might need to be convinced to # install to the egg build directory + INSTALL_DIRECTORY_ATTRS = [ 'install_lib', 'install_dir', 'install_data', 'install_base' ] diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 73bdb0cb..5ce529b2 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1831,6 +1831,7 @@ def _remove_and_clear_zip_directory_cache_data(normalized_path): normalized_path, zipimport._zip_directory_cache, updater=clear_and_remove_cached_zip_archive_directory_data) + # PyPy Python implementation does not allow directly writing to the # zipimport._zip_directory_cache and so prevents us from attempting to correct # its content. The best we can do there is clear the problematic cache content @@ -1990,6 +1991,7 @@ class CommandSpec(list): cmdline = subprocess.list2cmdline(items) return '#!' + cmdline + '\n' + # For pbr compat; will be removed in a future version. sys_executable = CommandSpec._sys_executable() diff --git a/setuptools/depends.py b/setuptools/depends.py index 75344590..d5a344ad 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -213,4 +213,5 @@ def _update_globals(): del globals()[name] __all__.remove(name) + _update_globals() diff --git a/setuptools/dist.py b/setuptools/dist.py index bfdbb3b5..0a192553 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -36,12 +36,14 @@ def _get_unpatched(cls): ) return cls + _Distribution = _get_unpatched(_Distribution) def _patch_distribution_metadata_write_pkg_file(): """Patch write_pkg_file to also write Requires-Python/Requires-External""" original_write = distutils.dist.DistributionMetadata.write_pkg_file + def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ @@ -50,6 +52,8 @@ def _patch_distribution_metadata_write_pkg_file(): file.write('Requires-Python: %s\n' % self.python_requires) distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file + + _patch_distribution_metadata_write_pkg_file() @@ -72,6 +76,8 @@ def _patch_distribution_metadata_write_pkg_info(): self.write_pkg_file(pkg_info) distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info + + _patch_distribution_metadata_write_pkg_info() sequence = tuple, list diff --git a/setuptools/extension.py b/setuptools/extension.py index d265b7a3..5ea72c06 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -28,6 +28,7 @@ def _have_cython(): pass return False + # for compatibility have_pyrex = _have_cython @@ -53,6 +54,7 @@ class Extension(_Extension): class Library(Extension): """Just like a regular Extension, but built as a library instead""" + distutils.core.Extension = Extension distutils.extension.Extension = Extension if 'distutils.command.build_ext' in sys.modules: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 77f5f96e..0ea09bd6 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -194,6 +194,7 @@ def unique_values(func): return unique_everseen(func(*args, **kwargs)) return wrapper + REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) # this line is here to fix emacs' cruddy broken syntax highlighting @@ -894,6 +895,7 @@ class PackageIndex(Environment): def warn(self, msg, *args): log.warn(msg, *args) + # This pattern matches a character entity reference (a decimal numeric # references, a hexadecimal numeric reference, or a named reference). entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub @@ -1059,6 +1061,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): return fp + # adding a timeout to avoid freezing package_index open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py index 7c60c90e..90cd695a 100644 --- a/setuptools/py26compat.py +++ b/setuptools/py26compat.py @@ -19,5 +19,6 @@ def strip_fragment(url): url, fragment = splittag(url) return url + if sys.version_info >= (2, 7): strip_fragment = lambda x: x diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 66aecc2a..57eb150b 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -11,6 +11,7 @@ def get_all_headers(message, key): """ return message.get_all(key) + if sys.version_info < (3,): def get_all_headers(message, key): return message.getheaders(key) diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index c89593b1..df630d3e 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -460,6 +460,7 @@ class DirectorySandbox(AbstractSandbox): self._violation("os.open", file, flags, mode, *args, **kw) return _os.open(file, flags, mode, *args, **kw) + WRITE_FLAGS = functools.reduce( operator.or_, [getattr(_os, a, 0) for a in "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] diff --git a/setuptools/site-patch.py b/setuptools/site-patch.py index a7d13fcd..f09ab522 100644 --- a/setuptools/site-patch.py +++ b/setuptools/site-patch.py @@ -68,6 +68,7 @@ def __boot(): sys.path[:] = new_path + if __name__ == 'site': __boot() del __boot diff --git a/setuptools/tests/py26compat.py b/setuptools/tests/py26compat.py index 5325dad6..18cece05 100644 --- a/setuptools/tests/py26compat.py +++ b/setuptools/tests/py26compat.py @@ -9,6 +9,7 @@ def _tarfile_open_ex(*args, **kwargs): """ return contextlib.closing(tarfile.open(*args, **kwargs)) + if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[:2] < (3, 2): tarfile_open = _tarfile_open_ex else: diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index aa905f5a..2c17ac76 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -50,6 +50,7 @@ class FakeDist(object): def as_requirement(self): return 'spec' + SETUP_PY = DALS(""" from setuptools import setup diff --git a/tests/manual_test.py b/tests/manual_test.py index 0904b607..9987e662 100644 --- a/tests/manual_test.py +++ b/tests/manual_test.py @@ -27,6 +27,7 @@ def tempdir(func): shutil.rmtree(test_dir) return _tempdir + SIMPLE_BUILDOUT = """\ [buildout] @@ -90,6 +91,7 @@ def test_full(): assert eggs == ['extensions-0.3-py2.6.egg', 'zc.recipe.egg-1.2.2-py2.6.egg'] + if __name__ == '__main__': test_virtualenv() test_full() -- cgit v1.2.1 From 789462de79c5f51ba24256d80ad2dac2e4fe2888 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 10:23:29 +0700 Subject: Add missing blank line. --- setuptools/lib2to3_ex.py | 1 + setuptools/msvc.py | 1 + setuptools/tests/test_bdist_egg.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py index 467e57d2..c8632bc5 100644 --- a/setuptools/lib2to3_ex.py +++ b/setuptools/lib2to3_ex.py @@ -10,6 +10,7 @@ This module raises an ImportError on Python 2. from distutils.util import Mixin2to3 as _Mixin2to3 from distutils import log from lib2to3.refactor import RefactoringTool, get_fixers_from_package + import setuptools diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 012ab32c..2a665c92 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -5,6 +5,7 @@ import os import platform import itertools import distutils.errors + from setuptools.extern.six.moves import filterfalse if platform.system() == 'Windows': diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index 5aabf404..c77aa226 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -9,6 +9,7 @@ from setuptools.dist import Distribution from . import contexts + SETUP_PY = """\ from setuptools import setup @@ -27,6 +28,7 @@ def setup_context(tmpdir): class Test: + def test_bdist_egg(self, setup_context, user_override): dist = Distribution(dict( script_name='setup.py', -- cgit v1.2.1 From d079163e74166860fedbfe0abcfcb820507368fd Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Sun, 17 Jul 2016 14:52:16 +0200 Subject: Also update the Metadata-Version when adding Requires-Python --- setuptools/dist.py | 38 +++++++++++++++++++++++++++++++++++++- setuptools/tests/test_egg_info.py | 6 ++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 0a192553..705ce9a3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -12,6 +12,7 @@ import distutils.dist from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) +from distutils.util import rfc822_escape from setuptools.extern import six from setuptools.extern.six.moves import map @@ -44,10 +45,45 @@ def _patch_distribution_metadata_write_pkg_file(): """Patch write_pkg_file to also write Requires-Python/Requires-External""" original_write = distutils.dist.DistributionMetadata.write_pkg_file + # Based on Python 3.5 version def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ - original_write(self, file) + version = '1.0' + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + version = '1.1' + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + version = '1.2' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) + if self.download_url: + file.write('Download-URL: %s\n' % self.download_url) + + long_desc = rfc822_escape(self.get_long_description()) + file.write('Description: %s\n' % long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + file.write('Keywords: %s\n' % keywords) + + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 if hasattr(self, 'python_requires'): file.write('Requires-Python: %s\n' % self.python_requires) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 0b9da538..dff2a8c8 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -223,8 +223,10 @@ class TestEggInfo(object): env=environ, ) egg_info_dir = os.path.join('.', 'foo.egg-info') - pkginfo = os.path.join(egg_info_dir, 'PKG-INFO') - assert 'Requires-Python: >=2.7.12' in open(pkginfo).read().split('\n') + with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: + pkg_info_lines = pkginfo_file.read().split('\n') + assert 'Requires-Python: >=2.7.12' in pkg_info_lines + assert 'Metadata-Version: 1.2' in pkg_info_lines def test_python_requires_install(self, tmpdir_cwd, env): self._setup_script_with_requires( -- cgit v1.2.1 From 6a628a665c968ea0b5d08cd4f769436e0aba15f2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 07:26:52 -0400 Subject: Remove unused variable --- setuptools/dist.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 705ce9a3..59a5ecf2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -43,7 +43,6 @@ _Distribution = _get_unpatched(_Distribution) def _patch_distribution_metadata_write_pkg_file(): """Patch write_pkg_file to also write Requires-Python/Requires-External""" - original_write = distutils.dist.DistributionMetadata.write_pkg_file # Based on Python 3.5 version def write_pkg_file(self, file): -- cgit v1.2.1 From b8d3bf71fae4829eceda274ccd322a8536bf3743 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 07:37:02 -0400 Subject: Adding preliminary support for enforcing style rules --- pytest.ini | 3 +++ setup.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/pytest.ini b/pytest.ini index 2fa3a3ec..c693a4c3 100755 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,6 @@ [pytest] addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern +flake8-ignore = + setuptools/site-patch.py F821 + setuptools/py*compat.py F811 diff --git a/setup.py b/setup.py index 7bd3a079..0aa394fb 100755 --- a/setup.py +++ b/setup.py @@ -161,6 +161,9 @@ setup_params = dict( scripts=[], tests_require=[ 'setuptools[ssl]', + 'pytest-flake8', + # workaround for pytest-flake8 #7 + 'flake8<3dev', 'pytest>=2.8', ] + (['mock'] if sys.version_info[:2] < (3, 3) else []), setup_requires=[ -- cgit v1.2.1 From c9a8458db8e767c833d881f1ed42118eed7ffed9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 07:40:43 -0400 Subject: Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 28924a97..63a634fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v24.2.1 +------- + +* #667: Update Metadata-Version to 1.2 when + ``python_requires`` is supplied. + v24.2.0 ------- -- cgit v1.2.1 From 2f33817e85c57237457159c65b77b7a1626d062f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 08:03:39 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.2.0=20=E2=86=92=2024.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index f94f03c8..8b067cdc 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.2.0 +current_version = 24.2.1 commit = True tag = True diff --git a/setup.py b/setup.py index 0aa394fb..73809f4c 100755 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.2.0", + version="24.2.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From be2684d801b99757d35345809d6a0343ce863d75 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 08:03:40 -0400 Subject: Added tag v24.2.1 for changeset 5b577d179a7e --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 85c47bdb..a05be9a8 100644 --- a/.hgtags +++ b/.hgtags @@ -284,3 +284,4 @@ d425bd1ee620772fe90e0dd2a7530b0d6a642601 v24.0.3 a7d2f79f0996d881794af0f87595032098202811 v24.1.0 d29075e7f8797891e8c59fb58c4d8d1b79954b34 v24.1.1 ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 +5b577d179a7e2f3020712c376c0200901e5c93c1 v24.2.1 -- cgit v1.2.1 From 9f77a068c02a3937762b8292c20e06d072247825 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 12:14:42 -0400 Subject: Disable os.link during make_distribution. Fixes #516. Note that better would be if sdist provided some sort of hooks to better control the file copying, but since it does not, this technique will suffice for now. --- CHANGES.rst | 8 ++++++++ setuptools/command/sdist.py | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 63a634fb..b01a6fad 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGES ======= +v24.3.0 +------- + +* #516: Disable ``os.link`` to avoid hard linking + in ``sdist.make_distribution``, avoiding errors on + systems that support hard links but not on the + file system in which the build is occurring. + v24.2.1 ------- diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 041ee42e..b86aae50 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -4,6 +4,7 @@ import distutils.command.sdist as orig import os import sys import io +import contextlib from setuptools.extern import six @@ -65,6 +66,32 @@ class sdist(orig.sdist): if data not in dist_files: dist_files.append(data) + def make_distribution(self): + """ + Workaround for #516 + """ + with self._remove_os_link(): + orig.sdist.make_distribution(self) + + @staticmethod + @contextlib.contextmanager + def _remove_os_link(): + """ + In a context, remove and restore os.link if it exists + """ + class NoValue: + pass + orig_val = getattr(os, 'link', NoValue) + try: + del os.link + except Exception: + pass + try: + yield + finally: + if orig_val is not NoValue: + setattr(os, 'link', orig_val) + def __read_template_hack(self): # This grody hack closes the template file (MANIFEST.in) if an # exception occurs during read_template. -- cgit v1.2.1 From 48f4155552b31928ef88b68e25e78db330b8b919 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 12:15:22 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.2.1=20=E2=86=92=2024.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 8b067cdc..50dee23b 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.2.1 +current_version = 24.3.0 commit = True tag = True diff --git a/setup.py b/setup.py index 73809f4c..d05ec741 100755 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.2.1", + version="24.3.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From e8d8a79224dd0e3b2af8a1f5708b06cc62e2b61a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 12:15:23 -0400 Subject: Added tag v24.3.0 for changeset 83ca05973c16 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index a05be9a8..53264d85 100644 --- a/.hgtags +++ b/.hgtags @@ -285,3 +285,4 @@ a7d2f79f0996d881794af0f87595032098202811 v24.1.0 d29075e7f8797891e8c59fb58c4d8d1b79954b34 v24.1.1 ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 5b577d179a7e2f3020712c376c0200901e5c93c1 v24.2.1 +83ca05973c16102145b339aec7e170d94966a2ba v24.3.0 -- cgit v1.2.1 From 7f118aaae6a49171a91248cc413dc78eb497a054 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 10:17:11 +0700 Subject: Fix continuation line unaligned for hanging indent. --- setuptools/dist.py | 6 +++--- setuptools/sandbox.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 0a192553..c539f6ef 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -542,19 +542,19 @@ class Distribution(_Distribution): if self.packages: self.packages = [ p for p in self.packages - if p != package and not p.startswith(pfx) + if p != package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ p for p in self.py_modules - if p != package and not p.startswith(pfx) + if p != package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ p for p in self.ext_modules - if p.name != package and not p.name.startswith(pfx) + if p.name != package and not p.name.startswith(pfx) ] def has_contents_for(self, package): diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index df630d3e..2babb636 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -258,7 +258,7 @@ class AbstractSandbox: def __init__(self): self._attrs = [ name for name in dir(_os) - if not name.startswith('_') and hasattr(self, name) + if not name.startswith('_') and hasattr(self, name) ] def _copy(self, source): -- cgit v1.2.1 From b3b8a8d106ecf9dbdb933f4f2a09ec65003b7d05 Mon Sep 17 00:00:00 2001 From: stepshal Date: Fri, 22 Jul 2016 12:56:21 +0700 Subject: Use 'except Exception:' instead of 'except:'. --- setuptools/command/easy_install.py | 4 ++-- setuptools/command/sdist.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 5ce529b2..468b9be7 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -431,7 +431,7 @@ class easy_install(Command): """ try: pid = os.getpid() - except: + except Exception: pid = random.randint(0, sys.maxsize) return os.path.join(self.install_dir, "test-easy-install-%s" % pid) @@ -929,7 +929,7 @@ class easy_install(Command): destination, fix_zipimporter_caches=new_dist_is_zipped, ) - except: + except Exception: update_dist_caches(destination, fix_zipimporter_caches=False) raise diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index b86aae50..b6125f58 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -99,7 +99,7 @@ class sdist(orig.sdist): # file. try: orig.sdist.read_template(self) - except: + except Exception: _, _, tb = sys.exc_info() tb.tb_next.tb_frame.f_locals['template'].close() raise -- cgit v1.2.1 From 4df6b3592b3a8e21006ee5d5b8e96ca685748954 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 14:50:42 -0400 Subject: Adding simple appveyor config --- appveyor.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..bd43fb2d --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,14 @@ +environment: + + matrix: + - PYTHON: "C:\\Python35-x64" + +install: + - "mklink /s /d \"C:\\Program Files\\Python\" %PYTHON%" + - "SET PYTHON=\"C:\\Program Files\\Python\"" + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + +build: off + +test_script: + - "%PYTHON%\\python setup.py test" -- cgit v1.2.1 From b1781f2786d062c6c4678f18b9355421297c6d47 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 14:53:18 -0400 Subject: Correct mklink usage. Symlink is the default. --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index bd43fb2d..35938480 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,8 @@ environment: - PYTHON: "C:\\Python35-x64" install: - - "mklink /s /d \"C:\\Program Files\\Python\" %PYTHON%" + # symlink python from a directory with a space + - "mklink /d \"C:\\Program Files\\Python\" %PYTHON%" - "SET PYTHON=\"C:\\Program Files\\Python\"" - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" -- cgit v1.2.1 From 299aaee1b09372f1735a640a849082cd8abda979 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 14:55:03 -0400 Subject: Need to bootstrap egg_info. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 35938480..4fdef703 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,4 +12,5 @@ install: build: off test_script: + - "%PYTHON%\\python bootstrap.py" - "%PYTHON%\\python setup.py test" -- cgit v1.2.1 From 0f6fa71ca87f0a849455fbffd18ec30081317a98 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 14:58:36 -0400 Subject: Should be able to execute without specifying the full path. --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 4fdef703..4ae6c791 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,5 +12,5 @@ install: build: off test_script: - - "%PYTHON%\\python bootstrap.py" - - "%PYTHON%\\python setup.py test" + - "python bootstrap.py" + - "python setup.py test" -- cgit v1.2.1 From 2dff41bc302204b1a30af94f40650da4aa23e063 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 15:06:50 -0400 Subject: Docs must be built from docs directory and not from root now. --- docs/developer-guide.txt | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index 7cd3c6d2..ad47cafd 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -111,15 +111,5 @@ Setuptools follows ``semver``. Building Documentation ---------------------- -Setuptools relies on the Sphinx system for building documentation and in -particular the ``build_sphinx`` distutils command. To build the -documentation, invoke:: - - python setup.py build_sphinx - -from the root of the repository. Setuptools will download a compatible -build of Sphinx and any requisite plugins and then build the -documentation in the build/sphinx directory. - -Setuptools does not support invoking the doc builder from the docs/ -directory as some tools expect. +Setuptools relies on the Sphinx system for building documentation. +To accommodate RTD, docs must be built from the docs/ directory. -- cgit v1.2.1 From b594b7c00dd43d3ae2b1a0b349cd0436fb8232d1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 15:09:05 -0400 Subject: Reference gitter in dev guide --- docs/developer-guide.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index ad47cafd..3ff77c25 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -32,8 +32,8 @@ User support and discussions are done through the issue tracker (for specific) issues, through the distutils-sig mailing list, or on IRC (Freenode) at #pypa. -Discussions about development happen on the pypa-dev mailing list or on IRC -(Freenode) at #pypa-dev. +Discussions about development happen on the pypa-dev mailing list or on +`Gitter `_. ----------------- Authoring Tickets -- cgit v1.2.1 From 451b84a0c56e23eea5396981747d570c80aef773 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jul 2016 15:16:19 -0400 Subject: Correct spelling --- setuptools/tests/test_msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index 2fbd56bf..9b5ffba0 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -71,7 +71,7 @@ class TestModulePatch: mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" - def test_no_registry_entryies_means_nothing_found(self): + def test_no_registry_entries_means_nothing_found(self): """ No registry entries or environment variable should lead to an error directing the user to download vcpython27. -- cgit v1.2.1 From 9fff1f8cdf26b67e8b5299f5295112c5ca5aa40b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:00:54 -0400 Subject: Add reference to pull request for functionality added in 18.3. --- CHANGES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index b01a6fad..c65bbce3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -520,7 +520,8 @@ v20.6.0 18.3 ---- -* Setuptools now allows disabling of the manipulation of the sys.path +* BB Pull Request #135: Setuptools now allows disabling of + the manipulation of the sys.path during the processing of the easy-install.pth file. To do so, set the environment variable ``SETUPTOOLS_SYS_PATH_TECHNIQUE`` to anything but "rewrite" (consider "raw"). During any install operation -- cgit v1.2.1 From 8b693597c1bbe6effb24ba209f1f36fd17ee9e90 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:11:35 -0400 Subject: Add docs on building docs --- docs/developer-guide.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index 3ff77c25..70e7f1cd 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -113,3 +113,8 @@ Building Documentation Setuptools relies on the Sphinx system for building documentation. To accommodate RTD, docs must be built from the docs/ directory. + +To build them, you need to have installed the requirements specified +in docs/requirements.txt. One way to do this is to use rwt: + + setuptools/docs$ python -m rwt -r requirements.txt -- -m sphinx . html -- cgit v1.2.1 From 3c9a71fa4e161500d45b64590274007871303280 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:28:50 -0400 Subject: Update changelog --- CHANGES.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index c65bbce3..97f46386 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,16 @@ CHANGES ======= +v24.3.1 +------- + +* #398: Fix shebang handling on Windows in script + headers where spaces in ``sys.executable`` would + produce an improperly-formatted shebang header, + introduced in 12.0 with the fix for #188. + +* #663, #670: More style updates. + v24.3.0 ------- -- cgit v1.2.1 From 0b10bf4ecc20b8a2ad033af53bd17d37b85ab60e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:29:11 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.3.0=20=E2=86=92=2024.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 50dee23b..52edb99c 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.3.0 +current_version = 24.3.1 commit = True tag = True diff --git a/setup.py b/setup.py index d05ec741..6863a900 100755 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.3.0", + version="24.3.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 9b058f7a10a4cca6541ef9f02b5a3a2703aacaea Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:29:11 -0400 Subject: Added tag v24.3.1 for changeset e14229dd2abc --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 53264d85..f8d0ce84 100644 --- a/.hgtags +++ b/.hgtags @@ -286,3 +286,4 @@ d29075e7f8797891e8c59fb58c4d8d1b79954b34 v24.1.1 ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 5b577d179a7e2f3020712c376c0200901e5c93c1 v24.2.1 83ca05973c16102145b339aec7e170d94966a2ba v24.3.0 +e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 -- cgit v1.2.1 From e211f4584ac59f404a3e1a17c6b54dff18468cf6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:17:27 -0400 Subject: Set sys path technique to raw by default. Fixes #674. --- CHANGES.rst | 19 +++++++++++++++++++ setuptools/command/easy_install.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 97f46386..83c10ce7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,25 @@ CHANGES ======= +v25.0.0 +------- + +* #674: Default ``sys.path`` manipulation by easy-install.pth + is now "raw", meaning that when writing easy-install.pth + during any install operation, the ``sys.path`` will not be + rewritten, giving preference to easy_installed packages. + + To retain the old behavior when using any easy_install + operation (including ``setup.py install`` when setuptools is + present), set the environment variable: + + SETUPTOOLS_SYS_PATH_TECHNIQUE=rewrite + + This project hopes that that few if any environments find it + necessary to retain the old behavior, and intends to drop + support for it altogether in a future release. Please report + any relevant concerns in the ticket for this change. + v24.3.1 ------- diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 19f8286b..0e0dc2c4 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1661,7 +1661,7 @@ class RewritePthDistributions(PthDistributions): """) -if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'rewrite') == 'rewrite': +if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite': PthDistributions = RewritePthDistributions -- cgit v1.2.1 From 2473c6f91d52699c050f83b8b68224e0ded581d7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:30:31 -0400 Subject: =?UTF-8?q?Bump=20version:=2024.3.1=20=E2=86=92=2025.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 52edb99c..1c72e124 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 24.3.1 +current_version = 25.0.0 commit = True tag = True diff --git a/setup.py b/setup.py index 6863a900..14b1b78d 100755 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ wheel = ['wheel'] if needs_wheel else [] setup_params = dict( name="setuptools", - version="24.3.1", + version="25.0.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 8a133bd9d91d7c40dabac05ad64f8a008f552f7c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 04:30:33 -0400 Subject: Added tag v25.0.0 for changeset 58e92028ab00 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f8d0ce84..4b814cda 100644 --- a/.hgtags +++ b/.hgtags @@ -287,3 +287,4 @@ ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 5b577d179a7e2f3020712c376c0200901e5c93c1 v24.2.1 83ca05973c16102145b339aec7e170d94966a2ba v24.3.0 e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 +58e92028ab0061f1f80d98e769c9143305275242 v25.0.0 -- cgit v1.2.1 From 2b8cf28f3be32903b79e5b5b579fab38105791dd Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sat, 23 Jul 2016 12:56:52 +0200 Subject: Exception will not raise because MSVC9 may be find by new behavior at setuptools/msvc.py line 171. --- setuptools/tests/test_msvc.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index 9b5ffba0..ad96752d 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -83,10 +83,12 @@ class TestModulePatch: with mock_reg(): assert find_vcvarsall(9.0) is None - expected = distutils.errors.DistutilsPlatformError - with pytest.raises(expected) as exc: + try: query_vcvarsall(9.0) - assert 'aka.ms/vcpython27' in str(exc) + except Exception as exc: + expected = distutils.errors.DistutilsPlatformError + assert isinstance(expected, exc) + assert 'aka.ms/vcpython27' in str(exc) @pytest.yield_fixture def user_preferred_setting(self): -- cgit v1.2.1 From 1a0644e065bfe2f05a64d92b4abd31392d471751 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Sat, 23 Jul 2016 13:01:13 +0200 Subject: Update test_msvc.py --- setuptools/tests/test_msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index ad96752d..a0c76ea0 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -87,7 +87,7 @@ class TestModulePatch: query_vcvarsall(9.0) except Exception as exc: expected = distutils.errors.DistutilsPlatformError - assert isinstance(expected, exc) + assert isinstance(exc, expected) assert 'aka.ms/vcpython27' in str(exc) @pytest.yield_fixture -- cgit v1.2.1 From 145acd3ccfb7aac2b9658ce8ef4f5cb3fa265d94 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:03:13 -0400 Subject: Only require the metadata when setup is invoked, not on import. Fixes #676. --- setup.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 14b1b78d..880d3f81 100755 --- a/setup.py +++ b/setup.py @@ -11,10 +11,11 @@ import textwrap # Allow to run setup.py from another directory. os.chdir(os.path.dirname(os.path.abspath(__file__))) -# Prevent improper installs without necessary metadata. See #659 -if not os.path.exists('setuptools.egg-info'): - msg = "Cannot build setuptools without metadata. Run bootstrap.py" - raise RuntimeError(msg) +def require_metadata(): + "Prevent improper installs without necessary metadata. See #659" + if not os.path.exists('setuptools.egg-info'): + msg = "Cannot build setuptools without metadata. Run bootstrap.py" + raise RuntimeError(msg) src_root = None @@ -171,4 +172,5 @@ setup_params = dict( ) if __name__ == '__main__': + require_metadata() dist = setuptools.setup(**setup_params) -- cgit v1.2.1 From 09ca40cc931c611952d344b96d1d6a5bfd6b502c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:22:14 -0400 Subject: Refactor setup script for nicer indentation and readability. --- setup.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 880d3f81..017a4119 100755 --- a/setup.py +++ b/setup.py @@ -56,14 +56,22 @@ readme_file = io.open('README.rst', encoding='utf-8') with readme_file: long_description = readme_file.read() -package_data = { - 'setuptools': ['script (dev).tmpl', 'script.tmpl', 'site-patch.py']} +package_data = dict( + setuptools=['script (dev).tmpl', 'script.tmpl', 'site-patch.py'], +) + force_windows_specific_files = ( os.environ.get("SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES") not in (None, "", "0") ) -if (sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt')) \ - or force_windows_specific_files: + +include_windows_files = ( + sys.platform == 'win32' or + os.name == 'java' and os._name == 'nt' or + force_windows_specific_files +) + +if include_windows_files: package_data.setdefault('setuptools', []).extend(['*.exe']) package_data.setdefault('setuptools.command', []).extend(['*.xml']) @@ -76,7 +84,7 @@ setup_params = dict( name="setuptools", version="25.0.0", description="Easily download, build, install, upgrade, and uninstall " - "Python packages", + "Python packages", author="Python Packaging Authority", author_email="distutils-sig@python.org", long_description=long_description, -- cgit v1.2.1 From cc5004500ea05e868f9a661500f3fd29a4d05037 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:26:53 -0400 Subject: Move imports to the top --- setup.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index 017a4119..a162872d 100755 --- a/setup.py +++ b/setup.py @@ -7,6 +7,9 @@ import io import os import sys import textwrap +from distutils.util import convert_path + +import setuptools # Allow to run setup.py from another directory. os.chdir(os.path.dirname(os.path.abspath(__file__))) @@ -19,16 +22,13 @@ def require_metadata(): src_root = None -from distutils.util import convert_path +def read_commands(): + command_ns = {} + init_path = convert_path('setuptools/command/__init__.py') + with open(init_path) as init_file: + exec(init_file.read(), command_ns) + return command_ns['__all__'] -command_ns = {} -init_path = convert_path('setuptools/command/__init__.py') -with open(init_path) as init_file: - exec(init_file.read(), command_ns) - -SETUP_COMMANDS = command_ns['__all__'] - -import setuptools scripts = [] @@ -101,7 +101,7 @@ setup_params = dict( entry_points={ "distutils.commands": [ "%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals() - for cmd in SETUP_COMMANDS + for cmd in read_commands() ], "distutils.setup_keywords": [ "eager_resources = setuptools.dist:assert_string_list", -- cgit v1.2.1 From 4842be2133a0438524cab3b8e2abada6df9e7c5d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:32:23 -0400 Subject: Compute dependency links, enabling short lines and codifying the path resolution. --- setup.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index a162872d..cff90888 100755 --- a/setup.py +++ b/setup.py @@ -80,6 +80,16 @@ pytest_runner = ['pytest-runner'] if needs_pytest else [] needs_wheel = set(['release', 'bdist_wheel']).intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] +def pypi_link(pkg_filename): + """ + Given the filename, including md5 fragment, construct the + dependency link for PyPI. + """ + root = 'https://pypi.python.org/packages/source' + name, sep, rest = pkg_filename.partition('-') + parts = root, name[0], name, pkg_filename + return '/'.join(parts) + setup_params = dict( name="setuptools", version="25.0.0", @@ -164,8 +174,12 @@ setup_params = dict( "certs": "certifi==2016.2.28", }, dependency_links=[ - 'https://pypi.python.org/packages/source/c/certifi/certifi-2016.2.28.tar.gz#md5=5d672aa766e1f773c75cfeccd02d3650', - 'https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2', + pypi_link( + 'certifi-2016.2.28.tar.gz#md5=5d672aa766e1f773c75cfeccd02d3650', + ), + pypi_link( + 'wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2', + ), ], scripts=[], tests_require=[ -- cgit v1.2.1 From 2eb1007f8d1a2cf6b60f19df665c5dade3d7c8da Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:35:41 -0400 Subject: Remove unused variable --- setup.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index cff90888..60653216 100755 --- a/setup.py +++ b/setup.py @@ -20,7 +20,6 @@ def require_metadata(): msg = "Cannot build setuptools without metadata. Run bootstrap.py" raise RuntimeError(msg) -src_root = None def read_commands(): command_ns = {} @@ -30,9 +29,6 @@ def read_commands(): return command_ns['__all__'] -scripts = [] - - def _gen_console_scripts(): yield "easy_install = setuptools.command.easy_install:main" @@ -49,11 +45,7 @@ def _gen_console_scripts(): .format(shortver=sys.version[:3])) -console_scripts = list(_gen_console_scripts()) - -readme_file = io.open('README.rst', encoding='utf-8') - -with readme_file: +with io.open('README.rst', encoding='utf-8') as readme_file: long_description = readme_file.read() package_data = dict( @@ -100,7 +92,7 @@ setup_params = dict( long_description=long_description, keywords="CPAN PyPI distutils eggs package management", url="https://github.com/pypa/setuptools", - src_root=src_root, + src_root=None, packages=setuptools.find_packages(exclude=['*.tests']), package_data=package_data, @@ -146,7 +138,7 @@ setup_params = dict( "depends.txt = setuptools.command.egg_info:warn_depends_obsolete", "dependency_links.txt = setuptools.command.egg_info:overwrite_arg", ], - "console_scripts": console_scripts, + "console_scripts": list(_gen_console_scripts()), "setuptools.installation": ['eggsecutable = setuptools.command.easy_install:bootstrap'], -- cgit v1.2.1 From bf866865418c7c58970b9f8da9151f7df3cba6af Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:44:59 -0400 Subject: Eliminate dependence on convert_path. Windows allows forward slashes, so just use them. --- setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 60653216..4eb3f78e 100755 --- a/setup.py +++ b/setup.py @@ -7,12 +7,13 @@ import io import os import sys import textwrap -from distutils.util import convert_path import setuptools +here = os.path.dirname(__file__) + # Allow to run setup.py from another directory. -os.chdir(os.path.dirname(os.path.abspath(__file__))) +here and os.chdir(here) def require_metadata(): "Prevent improper installs without necessary metadata. See #659" @@ -23,7 +24,8 @@ def require_metadata(): def read_commands(): command_ns = {} - init_path = convert_path('setuptools/command/__init__.py') + cmd_module_path = 'setuptools/command/__init__.py' + init_path = os.path.join(here, cmd_module_path) with open(init_path) as init_file: exec(init_file.read(), command_ns) return command_ns['__all__'] -- cgit v1.2.1 From 5a63f386217cae916c60cc8a89ae1115df9ea7f4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:51:20 -0400 Subject: Only chdir when executed, which may be unnecessary, but keeping it for compatibility. --- setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 4eb3f78e..355b0aaa 100755 --- a/setup.py +++ b/setup.py @@ -10,10 +10,9 @@ import textwrap import setuptools + here = os.path.dirname(__file__) -# Allow to run setup.py from another directory. -here and os.chdir(here) def require_metadata(): "Prevent improper installs without necessary metadata. See #659" @@ -47,7 +46,8 @@ def _gen_console_scripts(): .format(shortver=sys.version[:3])) -with io.open('README.rst', encoding='utf-8') as readme_file: +readme_path = os.path.join(here, 'README.rst') +with io.open(readme_path, encoding='utf-8') as readme_file: long_description = readme_file.read() package_data = dict( @@ -188,5 +188,7 @@ setup_params = dict( ) if __name__ == '__main__': + # allow setup.py to run from another directory + here and os.path.chdir(here) require_metadata() dist = setuptools.setup(**setup_params) -- cgit v1.2.1 From 8dea0697aa3089de31f96c02d734d9ed05d3dac9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 Jul 2016 13:56:57 -0400 Subject: Mark failing tests as xfail until they can be resolved. Ref #591. --- pkg_resources/tests/test_resources.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index 31847dc8..e0dbb652 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import os import sys import string +import platform from pkg_resources.extern.six.moves import map @@ -763,6 +764,9 @@ class TestNamespaces: pkg_resources._namespace_packages = saved_ns_pkgs sys.path = saved_sys_path + issue591 = pytest.mark.xfail(platform.system()=='Windows', reason="#591") + + @issue591 def test_two_levels_deep(self, symlinked_tmpdir): """ Test nested namespace packages @@ -796,6 +800,7 @@ class TestNamespaces: ] assert pkg1.pkg2.__path__ == expected + @issue591 def test_path_order(self, symlinked_tmpdir): """ Test that if multiple versions of the same namespace package subpackage -- cgit v1.2.1 From 52e41435776bf0d07de4e14a51944085b2235609 Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 24 Jul 2016 09:33:26 +0700 Subject: setup.py: add missing blank line. --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 355b0aaa..9df3da16 100755 --- a/setup.py +++ b/setup.py @@ -74,6 +74,7 @@ pytest_runner = ['pytest-runner'] if needs_pytest else [] needs_wheel = set(['release', 'bdist_wheel']).intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] + def pypi_link(pkg_filename): """ Given the filename, including md5 fragment, construct the @@ -84,6 +85,7 @@ def pypi_link(pkg_filename): parts = root, name[0], name, pkg_filename return '/'.join(parts) + setup_params = dict( name="setuptools", version="25.0.0", -- cgit v1.2.1 From 86689c912fe8d7c0c468e177f0f1f8b9260d4924 Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 24 Jul 2016 17:59:53 +0700 Subject: docs/conf.py: split multiple imports on one line. --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 07d6ad41..be010152 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,8 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # Allow Sphinx to find the setup command that is imported below, as referenced above. -import sys, os +import os +import sys sys.path.append(os.path.abspath('..')) import setup as setup_script -- cgit v1.2.1 From a8026f2911ed96c6a8f9a91e297e09af40bbce8c Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 24 Jul 2016 19:40:17 +0700 Subject: README: make exactly one space after period. --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index d8d8a6e5..2cf762d7 100755 --- a/README.rst +++ b/README.rst @@ -57,7 +57,7 @@ The recommended way to install setuptools on Windows is to download distribution file and install it for you. Once installation is complete, you will find an ``easy_install`` program in -your Python ``Scripts`` subdirectory. For simple invocation and best results, +your Python ``Scripts`` subdirectory. For simple invocation and best results, add this directory to your ``PATH`` environment variable, if it is not already present. If you did a user-local install, the ``Scripts`` subdirectory is ``$env:APPDATA\Python\Scripts``. @@ -134,7 +134,7 @@ Downloads ========= All setuptools downloads can be found at `the project's home page in the Python -Package Index`_. Scroll to the very bottom of the page to find the links. +Package Index`_. Scroll to the very bottom of the page to find the links. .. _the project's home page in the Python Package Index: https://pypi.python.org/pypi/setuptools @@ -175,9 +175,9 @@ learning about Setuptools, Python Eggs, and EasyInstall: * `The Internal Structure of Python Eggs`_ Questions, comments, and bug reports should be directed to the `distutils-sig -mailing list`_. If you have written (or know of) any tutorials, documentation, +mailing list`_. If you have written (or know of) any tutorials, documentation, plug-ins, or other resources for setuptools users, please let us know about -them there, so this reference list can be updated. If you have working, +them there, so this reference list can be updated. If you have working, *tested* patches to correct problems or add features, you may submit them to the `setuptools bug tracker`_. @@ -194,13 +194,13 @@ Credits ------- * The original design for the ``.egg`` format and the ``pkg_resources`` API was - co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first + co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first version of ``pkg_resources``, and supplied the OS X operating system version compatibility algorithm. * Ian Bicking implemented many early "creature comfort" features of easy_install, including support for downloading via Sourceforge and - Subversion repositories. Ian's comments on the Web-SIG about WSGI + Subversion repositories. Ian's comments on the Web-SIG about WSGI application deployment also inspired the concept of "entry points" in eggs, and he has given talks at PyCon and elsewhere to inform and educate the community about eggs and setuptools. @@ -215,7 +215,7 @@ Credits * Significant parts of the implementation of setuptools were funded by the Open Source Applications Foundation, to provide a plug-in infrastructure for the - Chandler PIM application. In addition, many OSAF staffers (such as Mike + Chandler PIM application. In addition, many OSAF staffers (such as Mike "Code Bear" Taylor) contributed their time and stress as guinea pigs for the use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!) @@ -224,7 +224,7 @@ Credits and addressed many defects. * Since the merge with Distribute, Jason R. Coombs is the - maintainer of setuptools. The project is maintained in coordination with + maintainer of setuptools. The project is maintained in coordination with the Python Packaging Authority (PyPA) and the larger Python community. .. _files: -- cgit v1.2.1 From 6156244cdeb74e39538516c9889596cbf994b68e Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 24 Jul 2016 21:44:15 +0700 Subject: CHANGES: make exactly one space after period. --- CHANGES.rst | 74 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 83c10ce7..5442f2a6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1250,12 +1250,12 @@ process to fail and PyPI uploads no longer accept files for 13.0. * Issue #125: Prevent Subversion support from creating a ~/.subversion directory just for checking the presence of a Subversion repository. -* Issue #12: Namespace packages are now imported lazily. That is, the mere +* Issue #12: Namespace packages are now imported lazily. That is, the mere declaration of a namespace package in an egg on ``sys.path`` no longer - causes it to be imported when ``pkg_resources`` is imported. Note that this + causes it to be imported when ``pkg_resources`` is imported. Note that this change means that all of a namespace package's ``__init__.py`` files must include a ``declare_namespace()`` call in order to ensure that they will be - handled properly at runtime. In 2.x it was possible to get away without + handled properly at runtime. In 2.x it was possible to get away without including the declaration, but only at the cost of forcing namespace packages to be imported early, which 3.0 no longer does. * Issue #148: When building (bdist_egg), setuptools no longer adds @@ -2036,7 +2036,7 @@ how it parses version numbers. * Distribute #67: Fixed doc typo (PEP 381/PEP 382). * Distribute no longer shadows setuptools if we require a 0.7-series - setuptools. And an error is raised when installing a 0.7 setuptools with + setuptools. And an error is raised when installing a 0.7 setuptools with distribute. * When run from within buildout, no attempt is made to modify an existing @@ -2336,7 +2336,7 @@ easy_install * Fix ``bdist_egg`` not including files in subdirectories of ``.egg-info``. * Allow ``.py`` files found by the ``include_package_data`` option to be - automatically included. Remove duplicate data file matches if both + automatically included. Remove duplicate data file matches if both ``include_package_data`` and ``package_data`` are used to refer to the same files. @@ -2377,7 +2377,7 @@ easy_install directory doesn't need to support .pth files. * ``MANIFEST.in`` is now forcibly closed when any errors occur while reading - it. Previously, the file could be left open and the actual error would be + it. Previously, the file could be left open and the actual error would be masked by problems trying to remove the open file on Windows systems. 0.6a10 @@ -2390,7 +2390,7 @@ easy_install * The ``sdist`` command no longer uses the traditional ``MANIFEST`` file to create source distributions. ``MANIFEST.in`` is still read and processed, - as are the standard defaults and pruning. But the manifest is built inside + as are the standard defaults and pruning. But the manifest is built inside the project's ``.egg-info`` directory as ``SOURCES.txt``, and it is rebuilt every time the ``egg_info`` command is run. @@ -2426,11 +2426,11 @@ easy_install command so that you can more easily wrap a "flat" egg in a system package. * Enhanced ``bdist_rpm`` so that it installs single-version eggs that - don't rely on a ``.pth`` file. The ``--no-egg`` option has been removed, + don't rely on a ``.pth`` file. The ``--no-egg`` option has been removed, since all RPMs are now built in a more backwards-compatible format. * Support full roundtrip translation of eggs to and from ``bdist_wininst`` - format. Running ``bdist_wininst`` on a setuptools-based package wraps the + format. Running ``bdist_wininst`` on a setuptools-based package wraps the egg in an .exe that will safely install it as an egg (i.e., with metadata and entry-point wrapper scripts), and ``easy_install`` can turn the .exe back into an ``.egg`` file or directory and install it as such. @@ -2454,19 +2454,19 @@ easy_install * Fixed some problems with fresh checkouts of projects that don't include ``.egg-info/PKG-INFO`` under revision control and put the project's source - code directly in the project directory. If such a package had any + code directly in the project directory. If such a package had any requirements that get processed before the ``egg_info`` command can be run, the setup scripts would fail with a "Missing 'Version:' header and/or PKG-INFO file" error, because the egg runtime interpreted the unbuilt metadata in a directory on ``sys.path`` (i.e. the current directory) as - being a corrupted egg. Setuptools now monkeypatches the distribution + being a corrupted egg. Setuptools now monkeypatches the distribution metadata cache to pretend that the egg has valid version information, until it has a chance to make it actually be so (via the ``egg_info`` command). 0.6a5 ----- - * Fixed missing gui/cli .exe files in distribution. Fixed bugs in tests. + * Fixed missing gui/cli .exe files in distribution. Fixed bugs in tests. 0.6a3 ----- @@ -2479,8 +2479,8 @@ easy_install ----- * Added ``console_scripts`` entry point group to allow installing scripts - without the need to create separate script files. On Windows, console - scripts get an ``.exe`` wrapper so you can just type their name. On other + without the need to create separate script files. On Windows, console + scripts get an ``.exe`` wrapper so you can just type their name. On other platforms, the scripts are written without a file extension. 0.6a1 @@ -2490,7 +2490,7 @@ easy_install the target package, using a ``--no-egg`` option. * The ``build_ext`` command now works better when using the ``--inplace`` - option and multiple Python versions. It now makes sure that all extensions + option and multiple Python versions. It now makes sure that all extensions match the current Python version, even if newer copies were built for a different Python version. @@ -2520,11 +2520,11 @@ easy_install * ``setuptools`` now finds its commands, ``setup()`` argument validators, and metadata writers using entry points, so that they can be extended by - third-party packages. See `Creating distutils Extensions + third-party packages. See `Creating distutils Extensions `_ for more details. - * The vestigial ``depends`` command has been removed. It was never finished + * The vestigial ``depends`` command has been removed. It was never finished or documented, and never would have worked without EasyInstall - which it pre-dated and was never compatible with. @@ -2558,9 +2558,9 @@ easy_install * ``setup.py install`` now automatically detects when an "unmanaged" package or module is going to be on ``sys.path`` ahead of a package being installed, - thereby preventing the newer version from being imported. If this occurs, + thereby preventing the newer version from being imported. If this occurs, a warning message is output to ``sys.stderr``, but installation proceeds - anyway. The warning message informs the user what files or directories + anyway. The warning message informs the user what files or directories need deleting, and advises them they can also use EasyInstall (with the ``--delete-conflicting`` option) to do it automatically. @@ -2581,14 +2581,14 @@ easy_install * The "egg_info" command now always sets the distribution metadata to "safe" forms of the distribution name and version, so that distribution files will be generated with parseable names (i.e., ones that don't include '-' in the - name or version). Also, this means that if you use the various ``--tag`` + name or version). Also, this means that if you use the various ``--tag`` options of "egg_info", any distributions generated will use the tags in the version, not just egg distributions. * Added support for defining command aliases in distutils configuration files, - under the "[aliases]" section. To prevent recursion and to allow aliases to + under the "[aliases]" section. To prevent recursion and to allow aliases to call the command of the same name, a given alias can be expanded only once - per command-line invocation. You can define new aliases with the "alias" + per command-line invocation. You can define new aliases with the "alias" command, either for the local, global, or per-user configuration. * Added "rotate" command to delete old distribution files, given a set of @@ -2596,7 +2596,7 @@ easy_install recently-modified distribution files matching each pattern.) * Added "saveopts" command that saves all command-line options for the current - invocation to the local, global, or per-user configuration file. Useful for + invocation to the local, global, or per-user configuration file. Useful for setting defaults without having to hand-edit a configuration file. * Added a "setopt" command that sets a single option in a specified distutils @@ -2615,7 +2615,7 @@ easy_install * Beefed up the "sdist" command so that if you don't have a MANIFEST.in, it will include all files under revision control (CVS or Subversion) in the current directory, and it will regenerate the list every time you create a - source distribution, not just when you tell it to. This should make the + source distribution, not just when you tell it to. This should make the default "do what you mean" more often than the distutils' default behavior did, while still retaining the old behavior in the presence of MANIFEST.in. @@ -2630,14 +2630,14 @@ easy_install 0.5a5 ----- - * Added ``develop`` command to ``setuptools``-based packages. This command + * Added ``develop`` command to ``setuptools``-based packages. This command installs an ``.egg-link`` pointing to the package's source directory, and script wrappers that ``execfile()`` the source versions of the package's - scripts. This lets you put your development checkout(s) on sys.path without + scripts. This lets you put your development checkout(s) on sys.path without having to actually install them. (To uninstall the link, use use ``setup.py develop --uninstall``.) - * Added ``egg_info`` command to ``setuptools``-based packages. This command + * Added ``egg_info`` command to ``setuptools``-based packages. This command just creates or updates the "projectname.egg-info" directory, without building an egg. (It's used by the ``bdist_egg``, ``test``, and ``develop`` commands.) @@ -2645,11 +2645,11 @@ easy_install * Enhanced the ``test`` command so that it doesn't install the package, but instead builds any C extensions in-place, updates the ``.egg-info`` metadata, adds the source directory to ``sys.path``, and runs the tests - directly on the source. This avoids an "unmanaged" installation of the + directly on the source. This avoids an "unmanaged" installation of the package to ``site-packages`` or elsewhere. * Made ``easy_install`` a standard ``setuptools`` command, moving it from - the ``easy_install`` module to ``setuptools.command.easy_install``. Note + the ``easy_install`` module to ``setuptools.command.easy_install``. Note that if you were importing or extending it, you must now change your imports accordingly. ``easy_install.py`` is still installed as a script, but not as a module. @@ -2660,10 +2660,10 @@ easy_install * Setup scripts using setuptools can now list their dependencies directly in the setup.py file, without having to manually create a ``depends.txt`` file. The ``install_requires`` and ``extras_require`` arguments to ``setup()`` - are used to create a dependencies file automatically. If you are manually + are used to create a dependencies file automatically. If you are manually creating ``depends.txt`` right now, please switch to using these setup arguments as soon as practical, because ``depends.txt`` support will be - removed in the 0.6 release cycle. For documentation on the new arguments, + removed in the 0.6 release cycle. For documentation on the new arguments, see the ``setuptools.dist.Distribution`` class. * Setup scripts using setuptools now always install using ``easy_install`` @@ -2672,7 +2672,7 @@ easy_install 0.5a1 ----- - * Added support for "self-installation" bootstrapping. Packages can now + * Added support for "self-installation" bootstrapping. Packages can now include ``ez_setup.py`` in their source distribution, and add the following to their ``setup.py``, in order to automatically bootstrap installation of setuptools as part of their setup process:: @@ -2692,21 +2692,21 @@ easy_install * All downloads are now managed by the ``PackageIndex`` class (which is now subclassable and replaceable), so that embedders can more easily override - download logic, give download progress reports, etc. The class has also + download logic, give download progress reports, etc. The class has also been moved to the new ``setuptools.package_index`` module. * The ``Installer`` class no longer handles downloading, manages a temporary - directory, or tracks the ``zip_ok`` option. Downloading is now handled + directory, or tracks the ``zip_ok`` option. Downloading is now handled by ``PackageIndex``, and ``Installer`` has become an ``easy_install`` command class based on ``setuptools.Command``. * There is a new ``setuptools.sandbox.run_setup()`` API to invoke a setup script in a directory sandbox, and a new ``setuptools.archive_util`` module - with an ``unpack_archive()`` API. These were split out of EasyInstall to + with an ``unpack_archive()`` API. These were split out of EasyInstall to allow reuse by other tools and applications. * ``setuptools.Command`` now supports reinitializing commands using keyword - arguments to set/reset options. Also, ``Command`` subclasses can now set + arguments to set/reset options. Also, ``Command`` subclasses can now set their ``command_consumes_arguments`` attribute to ``True`` in order to receive an ``args`` option containing the rest of the command line. @@ -2715,7 +2715,7 @@ easy_install * Added new options to ``bdist_egg`` to allow tagging the egg's version number with a subversion revision number, the current date, or an explicit tag - value. Run ``setup.py bdist_egg --help`` to get more information. + value. Run ``setup.py bdist_egg --help`` to get more information. * Misc. bug fixes -- cgit v1.2.1 From cfaab99763f94c1e73f8bc4c1a61648b70ecae72 Mon Sep 17 00:00:00 2001 From: stepshal Date: Mon, 25 Jul 2016 01:24:33 +0700 Subject: docs/conf.py: remove unused boilerplate. --- docs/conf.py | 97 ------------------------------------------------------------ 1 file changed, 97 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index be010152..dc0d8fae 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -37,9 +37,6 @@ templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.txt' -# The encoding of source files. -#source_encoding = 'utf-8' - # The master toctree document. master_doc = 'index' @@ -56,43 +53,13 @@ version = setup_script.setup_params['version'] # The full version, including alpha/beta/rc tags. release = setup_script.setup_params['version'] -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of documents that shouldn't be included in the build. -#unused_docs = [] - # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - # -- Options for HTML output --------------------------------------------------- @@ -100,11 +67,6 @@ pygments_style = 'sphinx' # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'nature' -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['_theme'] @@ -115,24 +77,6 @@ html_title = "Setuptools documentation" # A shorter title for the navigation bar. Default is the same as html_title. html_short_title = "Setuptools" -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = True @@ -140,42 +84,18 @@ html_use_smartypants = True # Custom sidebar templates, maps document names to template names. html_sidebars = {'index': 'indexsidebar.html'} -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - # If false, no module index is generated. html_use_modindex = False # If false, no index is generated. html_use_index = False -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - # Output file base name for HTML help builder. htmlhelp_basename = 'Setuptoolsdoc' # -- Options for LaTeX output -------------------------------------------------- -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ @@ -183,23 +103,6 @@ latex_documents = [ 'The fellowship of the packaging', 'manual'), ] -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_use_modindex = True - link_files = { '../CHANGES.rst': dict( using=dict( -- cgit v1.2.1 From 4d82b38241d6edc2dcc4264a9d474fc6aba8f61e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 08:50:38 -0400 Subject: Update changelog to more clearly indicate the effect of the change. --- CHANGES.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5442f2a6..acbd206b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,7 +8,8 @@ v25.0.0 * #674: Default ``sys.path`` manipulation by easy-install.pth is now "raw", meaning that when writing easy-install.pth during any install operation, the ``sys.path`` will not be - rewritten, giving preference to easy_installed packages. + rewritten and will no longer give preference to easy_installed + packages. To retain the old behavior when using any easy_install operation (including ``setup.py install`` when setuptools is -- cgit v1.2.1 From d70b7b47644c2436a50f9d8dc8627dc5809fb5b1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 08:54:19 -0400 Subject: Update changelog --- CHANGES.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index acbd206b..54b35023 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,17 @@ CHANGES ======= +v25.0.1 +------- + +* Cleanup of setup.py script. + +* Fixed documentation builders by allowing setup.py + to be imported without having bootstrapped the + metadata. + +* More style cleanup. See #677, #678, #679, #681, #685. + v25.0.0 ------- -- cgit v1.2.1 From 5bff01fe9b15cbec748d83f6c229a2b66b096729 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 08:54:25 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.0.0=20=E2=86=92=2025.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 1c72e124..881d2d1a 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.0.0 +current_version = 25.0.1 commit = True tag = True diff --git a/setup.py b/setup.py index 9df3da16..af5e3ac4 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.0.0", + version="25.0.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 21ab99e53f0c263a2210cf51525d6edcae1ae9a7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 08:54:26 -0400 Subject: Added tag v25.0.1 for changeset 0591bce16c7f --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 4b814cda..f75b16d2 100644 --- a/.hgtags +++ b/.hgtags @@ -288,3 +288,4 @@ ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 83ca05973c16102145b339aec7e170d94966a2ba v24.3.0 e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 58e92028ab0061f1f80d98e769c9143305275242 v25.0.0 +0591bce16c7f94191cea925929cc8b0ce6baca09 v25.0.1 -- cgit v1.2.1 From 0c1f6b6a53368a4b39f6617e13f50e11eac5bae1 Mon Sep 17 00:00:00 2001 From: stepshal Date: Mon, 25 Jul 2016 20:19:47 +0700 Subject: docs/easy_install.txt: remove repeated word. --- docs/easy_install.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/easy_install.txt b/docs/easy_install.txt index 591589fb..bd9f0e86 100644 --- a/docs/easy_install.txt +++ b/docs/easy_install.txt @@ -768,7 +768,7 @@ Command-Line Options run by scripts that are already installed. ``--user`` (New in 0.6.11) - Use the the user-site-packages as specified in :pep:`370` + Use the user-site-packages as specified in :pep:`370` instead of the global site-packages. ``--always-copy, -a`` (New in 0.5a4) -- cgit v1.2.1 From 1918a41831fbef6d4b67dc484c647d56658bae1d Mon Sep 17 00:00:00 2001 From: Elias Dorneles Date: Mon, 25 Jul 2016 10:38:51 -0300 Subject: fix: use os.chdir to change directory --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index af5e3ac4..e4d85b0b 100755 --- a/setup.py +++ b/setup.py @@ -191,6 +191,6 @@ setup_params = dict( if __name__ == '__main__': # allow setup.py to run from another directory - here and os.path.chdir(here) + here and os.chdir(here) require_metadata() dist = setuptools.setup(**setup_params) -- cgit v1.2.1 From 3fcd44be1e8d3d8b9f229333e8f43d4893658183 Mon Sep 17 00:00:00 2001 From: Jens Timmerman Date: Wed, 8 Jun 2016 16:56:06 +0200 Subject: check if a download is successfull before deciding not to try the next possible dist --- setuptools/package_index.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 0ea09bd6..8f8bf6ed 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -602,24 +602,26 @@ class PackageIndex(Environment): continue if dist in req and (dist.precedence <= SOURCE_DIST or not source): - return dist + mylocation = self.download(dist.location, tmpdir) + if os.path.exists(mylocation): + return dist, mylocation if force_scan: self.prescan() self.find_packages(requirement) - dist = find(requirement) + dist, mylocation = find(requirement) if local_index is not None: - dist = dist or find(requirement, local_index) + dist, mylocation = dist, mylocation if dist else find(requirement, local_index) if dist is None: if self.to_scan is not None: self.prescan() - dist = find(requirement) + dist, mylocation = find(requirement) if dist is None and not force_scan: self.find_packages(requirement) - dist = find(requirement) + dist, mylocation = find(requirement) if dist is None: self.warn( @@ -629,7 +631,7 @@ class PackageIndex(Environment): ) else: self.info("Best match: %s", dist) - return dist.clone(location=self.download(dist.location, tmpdir)) + return dist.clone(location=mylocation) def fetch(self, requirement, tmpdir, force_scan=False, source=False): """Obtain a file suitable for fulfilling `requirement` -- cgit v1.2.1 From 1e5bb5e680d00fcfc74078d0ea2d12760879b3c7 Mon Sep 17 00:00:00 2001 From: Jens Timmerman Date: Wed, 8 Jun 2016 18:04:00 +0200 Subject: addressed remarks, None, None since a tuple is expected --- setuptools/package_index.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 8f8bf6ed..a6918123 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -605,14 +605,15 @@ class PackageIndex(Environment): mylocation = self.download(dist.location, tmpdir) if os.path.exists(mylocation): return dist, mylocation + return None, None if force_scan: self.prescan() self.find_packages(requirement) dist, mylocation = find(requirement) - if local_index is not None: - dist, mylocation = dist, mylocation if dist else find(requirement, local_index) + if not dist and local_index is not None: + dist, mylocation = find(requirement, local_index) if dist is None: if self.to_scan is not None: -- cgit v1.2.1 From c4ef24f6bac7b4c12b3cfc71258524185d220fc7 Mon Sep 17 00:00:00 2001 From: Jens Timmerman Date: Fri, 24 Jun 2016 17:25:02 +0200 Subject: don't return a tuple, add a new attribute to current returnvalue --- setuptools/package_index.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index a6918123..b7247120 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -604,25 +604,25 @@ class PackageIndex(Environment): if dist in req and (dist.precedence <= SOURCE_DIST or not source): mylocation = self.download(dist.location, tmpdir) if os.path.exists(mylocation): - return dist, mylocation - return None, None + dist._location = mylocation + return dist if force_scan: self.prescan() self.find_packages(requirement) - dist, mylocation = find(requirement) + dist = find(requirement) if not dist and local_index is not None: - dist, mylocation = find(requirement, local_index) + dist = find(requirement, local_index) if dist is None: if self.to_scan is not None: self.prescan() - dist, mylocation = find(requirement) + dist = find(requirement) if dist is None and not force_scan: self.find_packages(requirement) - dist, mylocation = find(requirement) + dist = find(requirement) if dist is None: self.warn( @@ -632,7 +632,7 @@ class PackageIndex(Environment): ) else: self.info("Best match: %s", dist) - return dist.clone(location=mylocation) + return dist.clone(location=dist._location) def fetch(self, requirement, tmpdir, force_scan=False, source=False): """Obtain a file suitable for fulfilling `requirement` -- cgit v1.2.1 From e2124c84b83e8ad6adb3dd8e8ed846eea4f7601f Mon Sep 17 00:00:00 2001 From: Jens Timmerman Date: Fri, 24 Jun 2016 17:30:02 +0200 Subject: pick better names for variables --- setuptools/package_index.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index b7247120..89eb5786 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -602,9 +602,8 @@ class PackageIndex(Environment): continue if dist in req and (dist.precedence <= SOURCE_DIST or not source): - mylocation = self.download(dist.location, tmpdir) - if os.path.exists(mylocation): - dist._location = mylocation + dist.download_location = self.download(dist.location, tmpdir) + if os.path.exists(dist.download_location): return dist if force_scan: @@ -632,7 +631,7 @@ class PackageIndex(Environment): ) else: self.info("Best match: %s", dist) - return dist.clone(location=dist._location) + return dist.clone(location=dist.download_location) def fetch(self, requirement, tmpdir, force_scan=False, source=False): """Obtain a file suitable for fulfilling `requirement` -- cgit v1.2.1 From e2af2337adc2996e8aabaef2780dea8ac3e88487 Mon Sep 17 00:00:00 2001 From: Jens Timmerman Date: Fri, 24 Jun 2016 17:45:31 +0200 Subject: specify that no 'working' links were found, previous behaviour didn't check if downloads could succeed --- setuptools/package_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 89eb5786..8d965f49 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -625,7 +625,7 @@ class PackageIndex(Environment): if dist is None: self.warn( - "No local packages or download links found for %s%s", + "No local packages or working download links found for %s%s", (source and "a source distribution of " or ""), requirement, ) -- cgit v1.2.1 From 87c776227a5bf3443cddc9f26255ff7ab90d9829 Mon Sep 17 00:00:00 2001 From: Jens Timmerman Date: Mon, 25 Jul 2016 13:43:01 +0200 Subject: added changes to changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 54b35023..3a61f240 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,11 @@ v25.0.1 * More style cleanup. See #677, #678, #679, #681, #685. +* #609: setuptools will now try to download a distribution from + the next possible download location if the first download fails. + This means you can now specify multiple links as ``dependency_links`` + and all links will be tried until a working download link is encountered. + v25.0.0 ------- -- cgit v1.2.1 From 8009a4a87261708de5cdc2dada1508d9fcf51a35 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:39:32 -0400 Subject: Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 54b35023..a1ef21c2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v25.0.2 +------- + +* #688: Fix AttributeError in setup.py when invoked not from + the current directory. + v25.0.1 ------- -- cgit v1.2.1 From 192d3cb295be20ff3c28a79fb16210e024f23dd0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:39:39 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.0.1=20=E2=86=92=2025.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 881d2d1a..164ce513 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.0.1 +current_version = 25.0.2 commit = True tag = True diff --git a/setup.py b/setup.py index af5e3ac4..c0c166ea 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.0.1", + version="25.0.2", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From b15b40d3fdd88e6206de5d29c09fcc7e84779f8a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:39:40 -0400 Subject: Added tag v25.0.2 for changeset dc92db3e29a4 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index f75b16d2..4d75d103 100644 --- a/.hgtags +++ b/.hgtags @@ -289,3 +289,4 @@ ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 58e92028ab0061f1f80d98e769c9143305275242 v25.0.0 0591bce16c7f94191cea925929cc8b0ce6baca09 v25.0.1 +dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 -- cgit v1.2.1 From a4b7c659a63c924040fc03e96bf4befe4f11979d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:41:33 -0400 Subject: Added tag v25.0.2 for changeset 91eeaf2f33db --- .hgtags | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgtags b/.hgtags index 4d75d103..2acb7835 100644 --- a/.hgtags +++ b/.hgtags @@ -290,3 +290,5 @@ e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 58e92028ab0061f1f80d98e769c9143305275242 v25.0.0 0591bce16c7f94191cea925929cc8b0ce6baca09 v25.0.1 dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 +dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 +91eeaf2f33db99d8f78f8261931a1aea8fe8d952 v25.0.2 -- cgit v1.2.1 From 76a3d326724eb0157e7a3c67c38139639c78e9ee Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:48:05 -0400 Subject: Update changelog --- CHANGES.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4f366f74..5aa2733c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGES ======= +v25.1.0 +------- + +* #609: Setuptools will now try to download a distribution from + the next possible download location if the first download fails. + This means you can now specify multiple links as ``dependency_links`` + and all links will be tried until a working download link is encountered. + v25.0.2 ------- @@ -19,11 +27,6 @@ v25.0.1 * More style cleanup. See #677, #678, #679, #681, #685. -* #609: setuptools will now try to download a distribution from - the next possible download location if the first download fails. - This means you can now specify multiple links as ``dependency_links`` - and all links will be tried until a working download link is encountered. - v25.0.0 ------- -- cgit v1.2.1 From 168970b89c9ac421b153981f4ded731bf376a5cb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:48:10 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.0.2=20=E2=86=92=2025.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 164ce513..a9f2e021 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.0.2 +current_version = 25.1.0 commit = True tag = True diff --git a/setup.py b/setup.py index b03fd40a..be86c29c 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.0.2", + version="25.1.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 29016f7e696cdefba227ab761929698b4c636cb7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 Jul 2016 10:48:11 -0400 Subject: Added tag v25.1.0 for changeset 529a76a860c5 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 2acb7835..0e5e1b78 100644 --- a/.hgtags +++ b/.hgtags @@ -292,3 +292,4 @@ e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 91eeaf2f33db99d8f78f8261931a1aea8fe8d952 v25.0.2 +529a76a860c50d3cc262759b5b9ce28f171236f9 v25.1.0 -- cgit v1.2.1 From d506d3d7411e890028092b72f1005d487a5398f1 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Wed, 27 Jul 2016 19:13:51 +0200 Subject: Fix regression in commit #e404a4a Missing `,` --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 2a665c92..c762c28f 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -909,7 +909,7 @@ class EnvironmentInfo: os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'), os.path.join( ref, - 'Windows.Foundation.UniversalApiContract' + 'Windows.Foundation.UniversalApiContract', '1.0.0.0', ), os.path.join( @@ -919,7 +919,7 @@ class EnvironmentInfo: ), os.path.join( ref, - 'Windows.Networking.Connectivity.WwanContract' + 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0', ), os.path.join( -- cgit v1.2.1 From 6e0a29decbf6395e35f4b352e6d75d46b523727d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 Jul 2016 10:17:35 -0400 Subject: Also force newer version of flake8, required by pytest-flake8 0.6. --- .travis.yml | 2 +- appveyor.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e91f7e78..e275605b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: - LC_ALL=C LC_CTYPE=C script: # avoid VersionConflict when newer version is required - - pip install -U pytest + - pip install -U pytest flake8 # Output the env, because the travis docs just can't be trusted - env diff --git a/appveyor.yml b/appveyor.yml index 4ae6c791..14456813 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,5 +12,6 @@ install: build: off test_script: + - "python -m pip install -U flake8" - "python bootstrap.py" - "python setup.py test" -- cgit v1.2.1 From 93c69277ef3519239857263d8c996fce23335d38 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 Jul 2016 10:24:58 -0400 Subject: Maybe just the pin needs to be removed --- .travis.yml | 2 +- appveyor.yml | 1 - setup.py | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index e275605b..e91f7e78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: - LC_ALL=C LC_CTYPE=C script: # avoid VersionConflict when newer version is required - - pip install -U pytest flake8 + - pip install -U pytest # Output the env, because the travis docs just can't be trusted - env diff --git a/appveyor.yml b/appveyor.yml index 14456813..4ae6c791 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,6 +12,5 @@ install: build: off test_script: - - "python -m pip install -U flake8" - "python bootstrap.py" - "python setup.py test" diff --git a/setup.py b/setup.py index be86c29c..0cf1d0b2 100755 --- a/setup.py +++ b/setup.py @@ -181,8 +181,6 @@ setup_params = dict( tests_require=[ 'setuptools[ssl]', 'pytest-flake8', - # workaround for pytest-flake8 #7 - 'flake8<3dev', 'pytest>=2.8', ] + (['mock'] if sys.version_info[:2] < (3, 3) else []), setup_requires=[ -- cgit v1.2.1 From 10e1523a77919e0fd65098946aa1f6dcbceca087 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 27 Jul 2016 11:45:36 +0200 Subject: ignore .tox in pytest avoids errors in setup.py test if tox has been run --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index c693a4c3..640716a6 100755 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py -norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern +norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .tox flake8-ignore = setuptools/site-patch.py F821 setuptools/py*compat.py F811 -- cgit v1.2.1 From 279e625938d7991755c0aeba63f4293d9b67fa0f Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 27 Jul 2016 13:35:44 +0200 Subject: only insert eggs ahead of parent if not already on path in `insert_on(replace=False)` and set `replace=False` by default in `activate()` Fixes pkg_resources modifying sys.path for all eggs with SETUPTOOLS_SYS_PATH_TECHINQUE=raw --- pkg_resources/__init__.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 6337db99..602549be 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2503,11 +2503,11 @@ class Distribution(object): for line in self.get_metadata_lines(name): yield line - def activate(self, path=None): + def activate(self, path=None, replace=False): """Ensure distribution is importable on `path` (default=sys.path)""" if path is None: path = sys.path - self.insert_on(path, replace=True) + self.insert_on(path, replace=replace) if path is sys.path: fixup_namespace_packages(self.location) for pkg in self._get_metadata('namespace_packages.txt'): @@ -2585,7 +2585,24 @@ class Distribution(object): return self.get_entry_map(group).get(name) def insert_on(self, path, loc=None, replace=False): - """Insert self.location in path before its nearest parent directory""" + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ loc = loc or self.location if not loc: @@ -2600,6 +2617,9 @@ class Distribution(object): break elif item == bdir and self.precedence == EGG_DIST: # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return if path is sys.path: self.check_version_conflict() path.insert(p, loc) -- cgit v1.2.1 From 4d158f842cbadb0d4b3bbeee3a8488f5f9a3dee4 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 27 Jul 2016 14:42:38 +0200 Subject: only call `dist.activate(True)` for *new* dists, not those loaded by default. --- pkg_resources/__init__.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 602549be..6aabd4c5 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -947,11 +947,17 @@ class WorkingSet(object): return needed - def subscribe(self, callback): - """Invoke `callback` for all distributions (including existing ones)""" + def subscribe(self, callback, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ if callback in self.callbacks: return self.callbacks.append(callback) + if not existing: + return for dist in self: callback(dist) @@ -2967,10 +2973,14 @@ def _initialize_master_working_set(): run_script = working_set.run_script # backward compatibility run_main = run_script - # Activate all distributions already on sys.path, and ensure that - # all distributions added to the working set in the future (e.g. by - # calling ``require()``) will get activated as well. - add_activation_listener(lambda dist: dist.activate()) + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + for dist in working_set: + dist.activate(replace=False) + del dist + add_activation_listener(lambda dist: dist.activate(replace=True), existing=False) working_set.entries=[] # match order list(map(working_set.add_entry, sys.path)) -- cgit v1.2.1 From 58c02d28c216d4195429b220d14ba7326e93a62b Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 27 Jul 2016 15:10:53 +0200 Subject: insert(replace=False) should really be a no-op if path is found that includes removing duplicates. --- pkg_resources/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 6aabd4c5..6d472734 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2620,7 +2620,11 @@ class Distribution(object): for p, item in enumerate(npath): if item == nloc: - break + if replace: + break + else: + # don't modify path (even removing duplicates) if found and not replace + return elif item == bdir and self.precedence == EGG_DIST: # if it's an .egg, give it precedence over its directory # UNLESS it's already been added to sys.path and replace=False -- cgit v1.2.1 From 9e3f3cb5f58caf69e003023d724a8793e8d1c184 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 Jul 2016 15:00:01 -0400 Subject: Update changelog --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5aa2733c..77bd1c79 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v25.1.1 +------- + +* #686: Fix issue in sys.path ordering by pkg_resources when + rewrite technique is "raw". +* #699: Fix typo in msvc support. + v25.1.0 ------- -- cgit v1.2.1 From ced5db7822ece3d4e4822b12dde5a67a6a60e90b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 Jul 2016 15:00:06 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.0=20=E2=86=92=2025.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index a9f2e021..25d403ee 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.0 +current_version = 25.1.1 commit = True tag = True diff --git a/setup.py b/setup.py index 0cf1d0b2..1b18ab92 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.0", + version="25.1.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 7cb842e1e81cbff7e1a0e04a46e1da96113f9a15 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 28 Jul 2016 15:00:06 -0400 Subject: Added tag v25.1.1 for changeset 392ee093902e --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 0e5e1b78..cf704a43 100644 --- a/.hgtags +++ b/.hgtags @@ -293,3 +293,4 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 91eeaf2f33db99d8f78f8261931a1aea8fe8d952 v25.0.2 529a76a860c50d3cc262759b5b9ce28f171236f9 v25.1.0 +392ee093902e14a1d2a6eefc389a7b9ac78b3f9e v25.1.1 -- cgit v1.2.1 From ba535ea4958e17a2cb2e79df093f50d057c07562 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Thu, 28 Jul 2016 23:03:45 +0200 Subject: Fix for #646 --- setuptools/msvc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index c762c28f..6fb3300b 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -449,10 +449,14 @@ class RegistryInfo: for hkey in self.HKEYS: try: bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + except OSError: + continue except IOError: continue try: return winreg.QueryValueEx(bkey, name)[0] + except OSError: + pass except IOError: pass -- cgit v1.2.1 From ca9749b3c9b6ec565686347db0e47cd272a4c524 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 29 Jul 2016 10:09:43 +0200 Subject: fix NameError when initial working_set is empty by defining dist so there's something to delete --- pkg_resources/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 6d472734..9f0ea6ec 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2981,6 +2981,7 @@ def _initialize_master_working_set(): # ensure that all distributions added to the working set in the future # (e.g. by calling ``require()``) will get activated as well, # with higher priority (replace=True). + dist = None # ensure dist is defined for del dist below for dist in working_set: dist.activate(replace=False) del dist -- cgit v1.2.1 From 15e0f14c1a7275a944c8c0c3f0f0e4badefa646a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Jul 2016 23:54:30 -0400 Subject: Remove copy of rmtree, as Python 2.3 is no longer supported. --- setuptools/command/easy_install.py | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 0e0dc2c4..ae9079d8 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2230,39 +2230,7 @@ def load_launcher_manifest(name): def rmtree(path, ignore_errors=False, onerror=auto_chmod): - """Recursively delete a directory tree. - - This code is taken from the Python 2.4 version of 'shutil', because - the 2.3 version doesn't really work right. - """ - if ignore_errors: - def onerror(*args): - pass - elif onerror is None: - def onerror(*args): - raise - names = [] - try: - names = os.listdir(path) - except os.error: - onerror(os.listdir, path, sys.exc_info()) - for name in names: - fullname = os.path.join(path, name) - try: - mode = os.lstat(fullname).st_mode - except os.error: - mode = 0 - if stat.S_ISDIR(mode): - rmtree(fullname, ignore_errors, onerror) - else: - try: - os.remove(fullname) - except os.error: - onerror(os.remove, fullname, sys.exc_info()) - try: - os.rmdir(path) - except os.error: - onerror(os.rmdir, path, sys.exc_info()) + return shutil.rmtree(path, ignore_errors, onerror) def current_umask(): -- cgit v1.2.1 From 849b034ac5440def5327172fa799fbbb3d40db98 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 08:11:33 -0400 Subject: Don't rely on mock in the plugin --- setuptools/tests/fixtures.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/setuptools/tests/fixtures.py b/setuptools/tests/fixtures.py index c70c38cb..1e81ee26 100644 --- a/setuptools/tests/fixtures.py +++ b/setuptools/tests/fixtures.py @@ -1,22 +1,18 @@ -try: - from unittest import mock -except ImportError: - import mock import pytest from . import contexts @pytest.yield_fixture -def user_override(): +def user_override(monkeypatch): """ Override site.USER_BASE and site.USER_SITE with temporary directories in a context. """ with contexts.tempdir() as user_base: - with mock.patch('site.USER_BASE', user_base): + monkeypatch.setattr('site.USER_BASE', user_base) with contexts.tempdir() as user_site: - with mock.patch('site.USER_SITE', user_site): + monkeypatch.setattr('site.USER_SITE', user_site) with contexts.save_user_site_setting(): yield -- cgit v1.2.1 From 1074affc4a94224f30692db4ab0d45ed29ac8c8d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 08:12:18 -0400 Subject: Reindent --- setuptools/tests/fixtures.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setuptools/tests/fixtures.py b/setuptools/tests/fixtures.py index 1e81ee26..5204c8d1 100644 --- a/setuptools/tests/fixtures.py +++ b/setuptools/tests/fixtures.py @@ -10,11 +10,11 @@ def user_override(monkeypatch): a context. """ with contexts.tempdir() as user_base: - monkeypatch.setattr('site.USER_BASE', user_base) - with contexts.tempdir() as user_site: - monkeypatch.setattr('site.USER_SITE', user_site) - with contexts.save_user_site_setting(): - yield + monkeypatch.setattr('site.USER_BASE', user_base) + with contexts.tempdir() as user_site: + monkeypatch.setattr('site.USER_SITE', user_site) + with contexts.save_user_site_setting(): + yield @pytest.yield_fixture -- cgit v1.2.1 From 7317122ae9092ab72d1ae82bad1afc887fca6508 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 10:42:13 -0400 Subject: Add test capturing undesirable behavior when unicode characters appear in the filename of a zip sdist. Ref #704. --- setuptools/tests/test_easy_install.py | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 11299c7c..b7429d49 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -135,6 +135,57 @@ class TestEasyInstallTest: monkeypatch.delattr(site, 'getsitepackages', raising=False) assert ei.get_site_dirs() + @pytest.fixture + def sdist_unicode(self, tmpdir): + files = [ + ( + 'setup.py', + DALS(""" + import setuptools + setuptools.setup( + name="setuptools-test-unicode", + version="1.0", + packages=["mypkg"], + include_package_data=True, + ) + """), + ), + ( + 'mypkg/__init__.py', + "", + ), + ( + u'mypkg/\u2603.txt', + "", + ), + ] + sdist_name = 'setuptools-test-unicode-1.0.zip' + sdist = tmpdir / sdist_name + import zipfile + # can't use make_sdist, because the issue only occurs + # with zip sdists. + sdist_zip = zipfile.ZipFile(str(sdist), 'w') + for filename, content in files: + sdist_zip.writestr(filename, content) + sdist_zip.close() + return str(sdist) + + def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): + """ + The install command should execute correctly even if + the package has unicode filenames. + """ + dist = Distribution({'script_args': ['easy_install']}) + target = (tmpdir / 'target').ensure_dir() + cmd = ei.easy_install( + dist, + install_dir=str(target), + args=['x'], + ) + monkeypatch.setitem(os.environ, 'PYTHONPATH', str(target)) + cmd.ensure_finalized() + cmd.easy_install(sdist_unicode) + class TestPTHFileWriter: -- cgit v1.2.1 From 3f6926a9b069f48f22dd74d80733d62433e54162 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 10:44:45 -0400 Subject: Include Python 2.7 in the appveyor matrix to capture failures on older Pythons. Ref #704. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 4ae6c791..f3d22f09 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,7 @@ environment: matrix: - PYTHON: "C:\\Python35-x64" + - PYTHON: "C:\\Python27-x64" install: # symlink python from a directory with a space -- cgit v1.2.1 From 2e8a63bd67e3f7261a5999503269465c58611f48 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 10:52:32 -0400 Subject: Mark test as xfail. Ref #706. --- setuptools/tests/test_easy_install.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index b7429d49..161c673f 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -170,6 +170,8 @@ class TestEasyInstallTest: sdist_zip.close() return str(sdist) + @pytest.mark.xfail(os.environ.get('LANG') == 'C', + reason="https://github.com/pypa/setuptools/issues/706") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 5ef93a05518c3cc6b48f45d1a7213fcb69df90b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 10:53:05 -0400 Subject: Move import to top --- setuptools/tests/test_easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 161c673f..0f1ab253 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -14,6 +14,7 @@ import logging import itertools import distutils.errors import io +import zipfile import time from setuptools.extern.six.moves import urllib @@ -161,7 +162,6 @@ class TestEasyInstallTest: ] sdist_name = 'setuptools-test-unicode-1.0.zip' sdist = tmpdir / sdist_name - import zipfile # can't use make_sdist, because the issue only occurs # with zip sdists. sdist_zip = zipfile.ZipFile(str(sdist), 'w') -- cgit v1.2.1 From 857949575022946cc60c7cd1d0d088246d3f7540 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:05:33 -0400 Subject: Ensure that tmpdir is unicode. Fixes #704. --- setuptools/command/easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index ae9079d8..4783ce48 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -627,7 +627,7 @@ class easy_install(Command): ) def easy_install(self, spec, deps=False): - tmpdir = tempfile.mkdtemp(prefix="easy_install-") + tmpdir = tempfile.mkdtemp(prefix=six.u("easy_install-")) if not self.editable: self.install_site_py() -- cgit v1.2.1 From 82d6b404903bebabb8ae3df2453001d0fb086e6b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:07:48 -0400 Subject: Use the logic already vetted for determining ascii context. --- setuptools/tests/test_easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 0f1ab253..b2094901 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -170,7 +170,7 @@ class TestEasyInstallTest: sdist_zip.close() return str(sdist) - @pytest.mark.xfail(os.environ.get('LANG') == 'C', + @pytest.mark.xfail(setuptools.tests.is_ascii, reason="https://github.com/pypa/setuptools/issues/706") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ -- cgit v1.2.1 From d38321d4bbb2f04de89706cd7274397cbfc879fb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:12:31 -0400 Subject: For now mark test as xfail until the issue can be diagnosed and resolved. Ref #707. --- setuptools/tests/test_msvc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index a0c76ea0..0d0a90f5 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -6,6 +6,8 @@ import os import contextlib import distutils.errors +import six + import pytest try: from unittest import mock @@ -71,6 +73,8 @@ class TestModulePatch: mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" + @pytest.mark.xfail(six.PY2, + reason="https://github.com/pypa/setuptools/issues/707") def test_no_registry_entries_means_nothing_found(self): """ No registry entries or environment variable should lead to an error -- cgit v1.2.1 From 0e4c33da98882fee354d21082103392b26af3615 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:15:19 -0400 Subject: Update changelog --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 77bd1c79..13ffe9e7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v25.1.2 +------- + +* #704: Fix errors when installing a zip sdist which contained + files named with non-ascii characters on Windows would + crash the install when it attempted to clean up the build. + v25.1.1 ------- -- cgit v1.2.1 From 6571ce8b2152c0c6e1e7012fe07970e96484a1a7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:17:13 -0400 Subject: Report skips and xfails on CI builds --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e91f7e78..32b664d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ script: # update egg_info based on setup.py in checkout - python bootstrap.py - - python setup.py test --addopts='-rs' + - python setup.py test --addopts='-rsx' before_deploy: - export SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES=1 diff --git a/appveyor.yml b/appveyor.yml index f3d22f09..ebf446bf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,4 +14,4 @@ build: off test_script: - "python bootstrap.py" - - "python setup.py test" + - "python setup.py test --addopts='-rsx'" -- cgit v1.2.1 From 09234d3084739075e0aba59002419c341a59a47e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:19:38 -0400 Subject: Correct import --- setuptools/tests/test_msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index 0d0a90f5..8c7e17d3 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -6,7 +6,7 @@ import os import contextlib import distutils.errors -import six +from setuptools.extern import six import pytest try: -- cgit v1.2.1 From 39912c7e8c5160a9805ca5f55ff671ffe2e521cb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:27:14 -0400 Subject: Update changelog --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 13ffe9e7..bc2bf5b5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,8 @@ v25.1.2 * #704: Fix errors when installing a zip sdist which contained files named with non-ascii characters on Windows would crash the install when it attempted to clean up the build. +* #646: MSVC compatibility - catch errors properly in + RegistryInfo.lookup. v25.1.1 ------- -- cgit v1.2.1 From 6d37deba69a8483f0591594081429d67d68f6ff1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:28:56 -0400 Subject: Update changelog --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index bc2bf5b5..de70d041 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ v25.1.2 crash the install when it attempted to clean up the build. * #646: MSVC compatibility - catch errors properly in RegistryInfo.lookup. +* #702: Prevent UnboundLocalError when initial working_set + is empty. v25.1.1 ------- -- cgit v1.2.1 From 573a34e3ab68a73ba2407d4990e68eba43c964ca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Jul 2016 11:30:19 -0400 Subject: Added tag v25.1.2 for changeset 1cbb29c23543 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index cf704a43..597d07da 100644 --- a/.hgtags +++ b/.hgtags @@ -294,3 +294,4 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 91eeaf2f33db99d8f78f8261931a1aea8fe8d952 v25.0.2 529a76a860c50d3cc262759b5b9ce28f171236f9 v25.1.0 392ee093902e14a1d2a6eefc389a7b9ac78b3f9e v25.1.1 +1cbb29c235439331a76c7b6b5cf8701f763478d3 v25.1.2 -- cgit v1.2.1 From 6c93713d75f5888cc9654ec23fc05f5b7772b51a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Jul 2016 20:39:07 -0400 Subject: Rewrite test_dist_info using pytest fixtures. --- setuptools/tests/test_dist_info.py | 81 ++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index db3cb2e7..92ef6e8c 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -14,10 +14,47 @@ from .textwrap import DALS class TestDistInfo: - def test_distinfo(self): + metadata_template = DALS(""" + Metadata-Version: 1.2 + Name: {name} + {version} + Requires-Dist: splort (==4) + Provides-Extra: baz + Requires-Dist: quux (>=1.1); extra == 'baz' + """) + + @pytest.fixture + def metadata(self, tmpdir): + dist_info_name = 'VersionedDistribution-2.718.dist-info' + versioned = tmpdir / dist_info_name + versioned.mkdir() + filename = versioned / 'METADATA' + filename.write_text( + self.metadata_template.format( + name='VersionedDistribution', + version='', + ).replace('\n\n', '\n'), + encoding='utf-8', + ) + + dist_info_name = 'UnversionedDistribution.dist-info' + unversioned = tmpdir / dist_info_name + unversioned.mkdir() + filename = unversioned / 'METADATA' + filename.write_text( + self.metadata_template.format( + name='UnversionedDistribution', + version='Version: 0.3', + ), + encoding='utf-8', + ) + + return str(tmpdir) + + def test_distinfo(self, metadata): dists = dict( (d.project_name, d) - for d in pkg_resources.find_distributions(self.tmpdir) + for d in pkg_resources.find_distributions(metadata) ) assert len(dists) == 2, dists @@ -28,46 +65,14 @@ class TestDistInfo: assert versioned.version == '2.718' # from filename assert unversioned.version == '0.3' # from METADATA - def test_conditional_dependencies(self): + def test_conditional_dependencies(self, metadata): specs = 'splort==4', 'quux>=1.1' requires = list(map(pkg_resources.Requirement.parse, specs)) - for d in pkg_resources.find_distributions(self.tmpdir): + for d in pkg_resources.find_distributions(metadata): assert d.requires() == requires[:1] assert d.requires(extras=('baz',)) == [ requires[0], - pkg_resources.Requirement.parse('quux>=1.1;extra=="baz"')] + pkg_resources.Requirement.parse('quux>=1.1;extra=="baz"'), + ] assert d.extras == ['baz'] - - metadata_template = DALS(""" - Metadata-Version: 1.2 - Name: {name} - {version} - Requires-Dist: splort (==4) - Provides-Extra: baz - Requires-Dist: quux (>=1.1); extra == 'baz' - """) - - def setup_method(self, method): - self.tmpdir = tempfile.mkdtemp() - dist_info_name = 'VersionedDistribution-2.718.dist-info' - versioned = os.path.join(self.tmpdir, dist_info_name) - os.mkdir(versioned) - with open(os.path.join(versioned, 'METADATA'), 'w+') as metadata_file: - metadata = self.metadata_template.format( - name='VersionedDistribution', - version='', - ).replace('\n\n', '\n') - metadata_file.write(metadata) - dist_info_name = 'UnversionedDistribution.dist-info' - unversioned = os.path.join(self.tmpdir, dist_info_name) - os.mkdir(unversioned) - with open(os.path.join(unversioned, 'METADATA'), 'w+') as metadata_file: - metadata = self.metadata_template.format( - name='UnversionedDistribution', - version='Version: 0.3', - ) - metadata_file.write(metadata) - - def teardown_method(self, method): - shutil.rmtree(self.tmpdir) -- cgit v1.2.1 From 52f72bb8d233ca518f4782c5dd9569c6764c53ef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Jul 2016 20:44:13 -0400 Subject: Extract variable for content --- setuptools/tests/test_dist_info.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 92ef6e8c..122d94a0 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -29,25 +29,21 @@ class TestDistInfo: versioned = tmpdir / dist_info_name versioned.mkdir() filename = versioned / 'METADATA' - filename.write_text( - self.metadata_template.format( - name='VersionedDistribution', - version='', - ).replace('\n\n', '\n'), - encoding='utf-8', - ) + content = self.metadata_template.format( + name='VersionedDistribution', + version='', + ).replace('\n\n', '\n') + filename.write_text(content, encoding='utf-8') dist_info_name = 'UnversionedDistribution.dist-info' unversioned = tmpdir / dist_info_name unversioned.mkdir() filename = unversioned / 'METADATA' - filename.write_text( - self.metadata_template.format( - name='UnversionedDistribution', - version='Version: 0.3', - ), - encoding='utf-8', + content = self.metadata_template.format( + name='UnversionedDistribution', + version='Version: 0.3', ) + filename.write_text(content, encoding='utf-8') return str(tmpdir) -- cgit v1.2.1 From 8cc9cd3c76ee2ab313163cf2bee54e5ba31f9f6b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Jul 2016 20:56:26 -0400 Subject: Build metadata programmatically to avoid weird double-newline removal. --- setuptools/tests/test_dist_info.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 122d94a0..0d1aed03 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -14,34 +14,39 @@ from .textwrap import DALS class TestDistInfo: - metadata_template = DALS(""" + metadata_base = DALS(""" Metadata-Version: 1.2 - Name: {name} - {version} Requires-Dist: splort (==4) Provides-Extra: baz Requires-Dist: quux (>=1.1); extra == 'baz' """) + @classmethod + def build_metadata(cls, **kwargs): + lines = ( + '{key}: {value}\n'.format(**locals()) + for key, value in kwargs.items() + ) + return cls.metadata_base + ''.join(lines) + @pytest.fixture def metadata(self, tmpdir): dist_info_name = 'VersionedDistribution-2.718.dist-info' versioned = tmpdir / dist_info_name versioned.mkdir() filename = versioned / 'METADATA' - content = self.metadata_template.format( - name='VersionedDistribution', - version='', - ).replace('\n\n', '\n') + content = self.build_metadata( + Name='VersionedDistribution', + ) filename.write_text(content, encoding='utf-8') dist_info_name = 'UnversionedDistribution.dist-info' unversioned = tmpdir / dist_info_name unversioned.mkdir() filename = unversioned / 'METADATA' - content = self.metadata_template.format( - name='UnversionedDistribution', - version='Version: 0.3', + content = self.build_metadata( + Name='UnversionedDistribution', + Version='0.3', ) filename.write_text(content, encoding='utf-8') -- cgit v1.2.1 From ffce5a1fb043e4b17666fca6919cd94fabb3536a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 16:20:03 -0400 Subject: Remove unused imports --- setuptools/tests/test_dist_info.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 0d1aed03..094a97ea 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -1,9 +1,5 @@ """Test .dist-info style distributions. """ -import os -import shutil -import tempfile - from setuptools.extern.six.moves import map import pytest -- cgit v1.2.1 From 477ee99aa7229bdce83ca80049359284c6332f2d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 16:22:05 -0400 Subject: The future is here! (Fix test failures on Python 2.7 introduced in 8cc9cd3c76). --- setuptools/tests/test_dist_info.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 094a97ea..f7e7d2bf 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -1,5 +1,8 @@ """Test .dist-info style distributions. """ + +from __future__ import unicode_literals + from setuptools.extern.six.moves import map import pytest -- cgit v1.2.1 From f60a5de47aa0a821ad1175b4c54f71b65ed05d72 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 16:31:55 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.1=20=E2=86=92=2025.1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 25d403ee..b34560df 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.1 +current_version = 25.1.2 commit = True tag = True diff --git a/setup.py b/setup.py index 1b18ab92..202077d1 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.1", + version="25.1.2", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From a509a879308ccf0937d20751665aaf8cdb0d27f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 16:32:19 -0400 Subject: Added tag 25.1.2 for changeset c350190e7bbf --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 597d07da..fb280fed 100644 --- a/.hgtags +++ b/.hgtags @@ -295,3 +295,4 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 529a76a860c50d3cc262759b5b9ce28f171236f9 v25.1.0 392ee093902e14a1d2a6eefc389a7b9ac78b3f9e v25.1.1 1cbb29c235439331a76c7b6b5cf8701f763478d3 v25.1.2 +c350190e7bbf274e6728f14af7451b1fd3aaeba2 25.1.2 -- cgit v1.2.1 From 315cd94d90349748016c842a3d7f851c631b76b7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 21:23:21 -0400 Subject: Reorganize imports --- setuptools/archive_util.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index 24d4e7bb..b6411cc5 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -1,11 +1,5 @@ """Utilities for extracting common archive formats""" - -__all__ = [ - "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", - "UnrecognizedFormat", "extraction_drivers", "unpack_directory", -] - import zipfile import tarfile import os @@ -13,9 +7,16 @@ import shutil import posixpath import contextlib from distutils.errors import DistutilsError + from pkg_resources import ensure_directory, ContextualZipFile +__all__ = [ + "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", + "UnrecognizedFormat", "extraction_drivers", "unpack_directory", +] + + class UnrecognizedFormat(DistutilsError): """Couldn't recognize the archive type""" -- cgit v1.2.1 From 4b1f577d73bc9d66a82e6e5ad84af89c9dca5c49 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 22:06:49 -0400 Subject: Add test capturing #709. --- setuptools/tests/test_archive_util.py | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 setuptools/tests/test_archive_util.py diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py new file mode 100644 index 00000000..0c282b73 --- /dev/null +++ b/setuptools/tests/test_archive_util.py @@ -0,0 +1,52 @@ +import six + +import pytest + +from setuptools import archive_util + +@pytest.fixture +def tarfile_with_unicode(tmpdir): + """ + Create a tarfile containing only a file whose name is + a zero byte file called testimäge.png. + + TODO: Is it possible to generate this file programmatically? + """ + data = ( + b'\x1f\x8b\x08\x00R\xfe\x9fW\x00\x03\xed\xd6AO\x13A\x14\x00' + b'\xe0\x11b\x8c\x8d\' z\xf02\x07=x\xd9\xce\xcc\xce\xec\xd4\xc3' + b'&\xa21\xb4X\\lI\xa3r0\xc3v\xc1\x8a\xec6\xcbbz3\x1cH\x80x\x93' + b'x\xeaQ\x12\x13\x0f$z\xd5\x13\x17\xa9U\x8e&\xfc\x06\x13\xff' + b'\x82\xb3\xa5\n\xb6J!\x16\x8c\xf0\xbed2\x9d\xd7\xe9v\xba\xb3' + b'\xafo\x8c\xe4\xa8\xaa\xa4=U\xf4\xc2\xa4\xf1 \xf2f\xa3\xd2\xcc' + b'\xfa\xcb)\xcf(\xfbS\xa8K\x08!\x16\xe78\xee\xa5%\x1a=a\xdb' + b'\xe3\x06FLL\x99\xe4R#\x9cbB%\xa5\x1c\xe1J\xb7\x16\xb0\x97' + b'\xb9\xd9H\x85z)\x8fT\xa8\xdc\xe0\xcf\xf3\xf4\xb4\xc9\xc9=\xae' + b'\xb3\xfdS\xf0\xcf\xfe?\xc1$.\xab\xe8\xa1m\xb4\xed~\x82\x11' + b'\xec\xea\xb1gS.\t%&e,\x8e\xa9\xdd1"S\tf\xe2\xfc\x8dt&{\xcf(zO' + b'lj\xe9]d\x8c\xec\n\x97\xfc\xc0\xe6\xdc\x12\x84\x9a2AS?\xc2\xfe' + b'\xe3\x92?m\xd3\xc4\xbf\xbe\x05\'Z\xfb\xbew\xff;:\xe5\xbf)dK\xfe' + b'\x0b*\x04\xc2\xa4\xfbKiw\xc2\xf3\x1f\x9d>\x7f\x06\xf5 4\xa2\\' + b'\xec\xe4\xf1]\xdc\x14\xc7\xd0Y\xdd\x98n\xefu\x8b\xc7\xdf\xf6w' + b'\xc9\xc1\xb1\xb1\\\xf3e\xfc\x89\xaan\xf9\x96)\xa7v\xe2\x17\xdc' + b'`\xc6(\x86Ay"\xa8\x18*\x8a\xc2\xd2\xc4\x9c~"\xf5\x9b\x95\xea' + b'\xeb\xc2\xf0\x95Z2][[t>\xd63\x9f\xb2K\x9b\x1b\xce\xed\xfa\x9d7' + b'\xb9W\x85\xdaH\xf6\xf3\x87z\xef\xd2\xe5\x17+\x03\xf3\x0b\xb9' + b'\xbe[}\xf3\xe7V\xab+h\xa8w\xbd\xecl\x86\xfd\xce\xf3\x9e/\xd7' + b'\x9e\xbe}\xb6|ih||uk\xeb>z\xb7v\xf1\xeb\xdf\xdf\xafcf\xa7\xfa' + b'\x1f\xde\xbf@\xc7\xfa\xcfEK\xfe[B\x10\xa8\xffGAW\xe9F\xfd\xefP' + b'\xfb\x894\x7f[\xfb\xcd\x14\xcef\xae\x0f\xe6tE/\xdc4\xdc\xd0' + b'\xd33\x02\xbf9\xcbJq}f\xe0t\xdf\'\x04~\x95\xeb0\x9c\x10\x8e' + b'\xd0a\xd7\xfeX\xa7\xfc\x8f\xf3\xe5\xd7\xfc\xe7\xc2\xe2P\xff' + b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\x008\xa8\xef\xb1g\xe1Z\x00(\x00\x00' + ) + target = tmpdir / 'unicode-pkg-1.0.tar.gz' + with open(str(target), mode='wb') as tf: + tf.write(data) + return str(target) + + +def test_unicode_files(tarfile_with_unicode, tmpdir): + target = tmpdir / 'out' + archive_util.unpack_archive(tarfile_with_unicode, six.text_type(target)) -- cgit v1.2.1 From 96159fc86c3ae19afe63e4699430f600c407f4d3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 22:17:36 -0400 Subject: Add coding header --- setuptools/tests/test_archive_util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index 0c282b73..1936d2a5 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -1,3 +1,5 @@ +# coding: utf-8 + import six import pytest -- cgit v1.2.1 From 1b8dc8c7ac45662f7719b1bd531d0575e7fb1f02 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 22:30:09 -0400 Subject: Fix UnicodeDecodeError when tarfile names have non-ascii characters. Fixes #709. --- setuptools/archive_util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index b6411cc5..a1960be8 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -143,6 +143,8 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): tarobj.chown = lambda *args: None for member in tarobj: name = member.name + if isinstance(name, bytes): + name = name.decode(tarfile.ENCODING) # don't extract absolute paths or ones with .. in them if not name.startswith('/') and '..' not in name.split('/'): prelim_dst = os.path.join(extract_dir, *name.split('/')) -- cgit v1.2.1 From 406885845bdb94abe7c6c63df1f1874e09a36d9d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 22:31:54 -0400 Subject: Update changelog. Ref #709. --- CHANGES.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index de70d041..9c85be31 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,10 +2,16 @@ CHANGES ======= +v25.1.3 +------- + +* #709: Fix errors when installing a tar.gz sdist that contained + files named with non-ascii characters on Python 2.7. + v25.1.2 ------- -* #704: Fix errors when installing a zip sdist which contained +* #704: Fix errors when installing a zip sdist that contained files named with non-ascii characters on Windows would crash the install when it attempted to clean up the build. * #646: MSVC compatibility - catch errors properly in -- cgit v1.2.1 From 12dc2c65240bb6d609db30a56dbb1fe217771a17 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 22:44:45 -0400 Subject: Get six from extern --- setuptools/tests/test_archive_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index 1936d2a5..3f67b2d4 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -1,11 +1,12 @@ # coding: utf-8 -import six +from setuptools.extern import six import pytest from setuptools import archive_util + @pytest.fixture def tarfile_with_unicode(tmpdir): """ -- cgit v1.2.1 From f9b2902f312834ca6676f0b79bedf845c2bc42c3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 1 Aug 2016 23:02:27 -0400 Subject: _extract_member needs final_dst to be a native string. Ref #710. --- setuptools/archive_util.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index a1960be8..6493b448 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -8,6 +8,8 @@ import posixpath import contextlib from distutils.errors import DistutilsError +from setuptools.extern import six + from pkg_resources import ensure_directory, ContextualZipFile @@ -164,6 +166,8 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): if final_dst: if final_dst.endswith(os.sep): final_dst = final_dst[:-1] + if six.PY2: + final_dst = final_dst.encode(tarfile.ENCODING) try: # XXX Ugh tarobj._extract_member(member, final_dst) -- cgit v1.2.1 From b822e86d4b41cd54c12e6d5fdf3f7d00e89bb4d4 Mon Sep 17 00:00:00 2001 From: Mehdi Abaakouk Date: Tue, 2 Aug 2016 13:31:38 +0200 Subject: Revert "Ensure that tmpdir is unicode. Fixes #704." This reverts commit 857949575022946cc60c7cd1d0d088246d3f7540. As we can see on https://github.com/pypa/setuptools/issues/709, this breaks many things (easy_install C extensions, all py3.5 tests, run with LANG=C). So instead of fixing in a hurry all new bugs due to this, I propose to revert this commit until all downsides of this change have been investigated. Related bug: https://github.com/pypa/setuptools/issues/709 Related bug: https://github.com/pypa/setuptools/issues/710 Related bug: https://github.com/pypa/setuptools/issues/712 --- setuptools/command/easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 4783ce48..ae9079d8 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -627,7 +627,7 @@ class easy_install(Command): ) def easy_install(self, spec, deps=False): - tmpdir = tempfile.mkdtemp(prefix=six.u("easy_install-")) + tmpdir = tempfile.mkdtemp(prefix="easy_install-") if not self.editable: self.install_site_py() -- cgit v1.2.1 From d12b01679cb0e2e6a3ea1832891614c6ee86095c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 08:54:51 -0400 Subject: Backout 1b8dc8c7ac. Reopens #709. --- setuptools/archive_util.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index 6493b448..16355092 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -145,8 +145,6 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): tarobj.chown = lambda *args: None for member in tarobj: name = member.name - if isinstance(name, bytes): - name = name.decode(tarfile.ENCODING) # don't extract absolute paths or ones with .. in them if not name.startswith('/') and '..' not in name.split('/'): prelim_dst = os.path.join(extract_dir, *name.split('/')) -- cgit v1.2.1 From 27e455c506748ad119813f2f8a23d1cc13726100 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 08:55:32 -0400 Subject: Backout f9b2902f312. Reopens #710. --- setuptools/archive_util.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index 16355092..b6411cc5 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -8,8 +8,6 @@ import posixpath import contextlib from distutils.errors import DistutilsError -from setuptools.extern import six - from pkg_resources import ensure_directory, ContextualZipFile @@ -164,8 +162,6 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): if final_dst: if final_dst.endswith(os.sep): final_dst = final_dst[:-1] - if six.PY2: - final_dst = final_dst.encode(tarfile.ENCODING) try: # XXX Ugh tarobj._extract_member(member, final_dst) -- cgit v1.2.1 From 0d0bc3003b3613318a382ff14917060cda43c9f0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 09:25:55 -0400 Subject: Mark tests as xfail. Ref #704 Ref #709 Ref #710. --- setuptools/tests/test_archive_util.py | 1 + setuptools/tests/test_easy_install.py | 1 + 2 files changed, 2 insertions(+) diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index 3f67b2d4..3e679c11 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -50,6 +50,7 @@ def tarfile_with_unicode(tmpdir): return str(target) +@pytest.mark.xfail(reason="#710 and #712") def test_unicode_files(tarfile_with_unicode, tmpdir): target = tmpdir / 'out' archive_util.unpack_archive(tarfile_with_unicode, six.text_type(target)) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index b2094901..9b10c428 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -172,6 +172,7 @@ class TestEasyInstallTest: @pytest.mark.xfail(setuptools.tests.is_ascii, reason="https://github.com/pypa/setuptools/issues/706") + @pytest.mark.xfail(reason="#709 and #710") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 1559cabbaf0bd537767bd0c13d875dc05a13720f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 09:27:50 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9c85be31..5a2b9928 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,8 +5,9 @@ CHANGES v25.1.3 ------- -* #709: Fix errors when installing a tar.gz sdist that contained - files named with non-ascii characters on Python 2.7. +* #714 and #704: Revert fix as it breaks other components + downstream that can't handle unicode. See #709, #710, + and #712. v25.1.2 ------- -- cgit v1.2.1 From ebe606c6b7925823ac98c7014f1fc61de3274b48 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 09:36:46 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.2=20=E2=86=92=2025.1.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index b34560df..4f08b809 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.2 +current_version = 25.1.3 commit = True tag = True diff --git a/setup.py b/setup.py index 202077d1..2309b7d2 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.2", + version="25.1.3", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From b0d667dbaedf16e77da46f420827d7493ed66d10 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 09:36:47 -0400 Subject: Added tag v25.1.3 for changeset 86e668badaf4 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index fb280fed..1b833185 100644 --- a/.hgtags +++ b/.hgtags @@ -296,3 +296,4 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 392ee093902e14a1d2a6eefc389a7b9ac78b3f9e v25.1.1 1cbb29c235439331a76c7b6b5cf8701f763478d3 v25.1.2 c350190e7bbf274e6728f14af7451b1fd3aaeba2 25.1.2 +86e668badaf45315bb8506ac2312665d129a0322 v25.1.3 -- cgit v1.2.1 From 3f54d45ad832984b0601efe6ff62f6d16a4de2a5 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 2 Aug 2016 18:26:02 +0200 Subject: Patch distutils._msvccompiler.library_dir_option Try to fix #694. --- setuptools/msvc.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 6fb3300b..419b2292 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -85,6 +85,11 @@ def patch_for_specialized_compiler(): except Exception: pass + try: + # Patch distutils._msvccompiler.library_dir_option + unpatched['msvc14_library_dir_option'] = msvc14compiler.library_dir_option + msvc14compiler.library_dir_option = msvc14_library_dir_option + def msvc9_find_vcvarsall(version): """ @@ -212,6 +217,12 @@ def msvc14_get_vc_env(plat_spec): raise +def msvc14_library_dir_option(dir): + if ' ' in dir: + dir = '"%s"' % dir + return unpatched['msvc14_library_dir_option'](dir) + + def _augment_exception(exc, version, arch=''): """ Add details to the exception message to help guide the user -- cgit v1.2.1 From 2214dbbd6fb407cf2313dba44558557072cf0974 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 2 Aug 2016 18:29:53 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 419b2292..2a746332 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -89,6 +89,8 @@ def patch_for_specialized_compiler(): # Patch distutils._msvccompiler.library_dir_option unpatched['msvc14_library_dir_option'] = msvc14compiler.library_dir_option msvc14compiler.library_dir_option = msvc14_library_dir_option + except Exception: + pass def msvc9_find_vcvarsall(version): -- cgit v1.2.1 From 2c4346bcc47ec1fd6fc77bd5bb4f760ed7c6667c Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 2 Aug 2016 18:42:24 +0200 Subject: Update msvc.py --- setuptools/msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 2a746332..785d879b 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -220,7 +220,7 @@ def msvc14_get_vc_env(plat_spec): def msvc14_library_dir_option(dir): - if ' ' in dir: + if ' ' in dir and '"' not in dir: dir = '"%s"' % dir return unpatched['msvc14_library_dir_option'](dir) -- cgit v1.2.1 From 8d1349a2291054d1048c0f1a7180a96817c5f427 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 12:47:41 -0400 Subject: Change order of xfail, giving the unfiltered one precedence. --- setuptools/tests/test_easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 9b10c428..27d703f5 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -170,9 +170,9 @@ class TestEasyInstallTest: sdist_zip.close() return str(sdist) + @pytest.mark.xfail(reason="#709 and #710") @pytest.mark.xfail(setuptools.tests.is_ascii, reason="https://github.com/pypa/setuptools/issues/706") - @pytest.mark.xfail(reason="#709 and #710") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 6a4e5446c941291ec5e7c56cee1d5a872300c955 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Aug 2016 13:10:24 -0400 Subject: Try only applying one xfail. --- setuptools/tests/test_easy_install.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 27d703f5..da0a355c 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -171,8 +171,9 @@ class TestEasyInstallTest: return str(sdist) @pytest.mark.xfail(reason="#709 and #710") - @pytest.mark.xfail(setuptools.tests.is_ascii, - reason="https://github.com/pypa/setuptools/issues/706") + # also + #@pytest.mark.xfail(setuptools.tests.is_ascii, + # reason="https://github.com/pypa/setuptools/issues/706") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 4732c1eb7830b7970ad545f3a4630a824650331b Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 2 Aug 2016 20:09:53 +0200 Subject: Fix #707 MSVC patch and Python 2 #707 Fix Python 2 Compatibility, and improve registry lookup (Key may not always be in 64bit registry node). --- setuptools/msvc.py | 67 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 6fb3300b..4616d4be 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -356,23 +356,12 @@ class RegistryInfo: def __init__(self, platform_info): self.pi = platform_info - @property - def microsoft(self): - """ - Microsoft software registry key. - """ - return os.path.join( - 'Software', - '' if self.pi.current_is_x86() else 'Wow6432Node', - 'Microsoft', - ) - @property def visualstudio(self): """ Microsoft Visual Studio root registry key. """ - return os.path.join(self.microsoft, 'VisualStudio') + return 'VisualStudio' @property def sxs(self): @@ -400,15 +389,14 @@ class RegistryInfo: """ Microsoft Visual C++ for Python registry key. """ - path = r'DevDiv\VCForPython' - return os.path.join(self.microsoft, path) + return r'DevDiv\VCForPython' @property def microsoft_sdk(self): """ Microsoft SDK registry key. """ - return os.path.join(self.microsoft, 'Microsoft SDKs') + return 'Microsoft SDKs' @property def windows_sdk(self): @@ -429,11 +417,29 @@ class RegistryInfo: """ Microsoft Windows Kits Roots registry key. """ - return os.path.join(self.microsoft, r'Windows Kits\Installed Roots') + return r'Windows Kits\Installed Roots' + + def microsoft(self, key, x86=False): + """ + Return key in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + x86: str + Force x86 software registry. + + Return + ------ + str: value + """ + node64 = '' if self.pi.current_is_x86() or x86 else r'\Wow6432Node' + return os.path.join('Software', node64, 'Microsoft', key) def lookup(self, key, name): """ - Look for values in registry. + Look for values in registry in Microsoft software registry. Parameters ---------- @@ -446,18 +452,23 @@ class RegistryInfo: ------ str: value """ + KEY_READ = winreg.KEY_READ + openkey = winreg.OpenKey + ms = self.microsoft for hkey in self.HKEYS: try: - bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) - except OSError: - continue - except IOError: - continue + bkey = openkey(hkey, ms(key), 0, KEY_READ) + except (OSError, IOError): + if not self.pi.current_is_x86(): + try: + bkey = openkey(hkey, ms(key, True), 0, KEY_READ) + except (OSError, IOError): + continue + else: + continue try: return winreg.QueryValueEx(bkey, name)[0] - except OSError: - pass - except IOError: + except (OSError, IOError): pass @@ -500,7 +511,7 @@ class SystemInfo: for key in vckeys: try: bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) - except IOError: + except (OSError, IOError): continue subkeys, values, _ = winreg.QueryInfoKey(bkey) for i in range(values): @@ -813,7 +824,7 @@ class EnvironmentInfo: Microsoft Visual C++ & Microsoft Foundation Class Includes """ return [os.path.join(self.si.VCInstallDir, 'Include'), - os.path.join(self.si.VCInstallDir, 'ATLMFC\Include')] + os.path.join(self.si.VCInstallDir, r'ATLMFC\Include')] @property def VCLibraries(self): @@ -1199,5 +1210,5 @@ class EnvironmentInfo: if name: return '%s\\' % name[0] return '' - except IOError: + except (OSError, IOError): return '' -- cgit v1.2.1 From 3ebd9363b715b4dd1a0aa1d89a596d08e4b41d84 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 2 Aug 2016 20:11:25 +0200 Subject: Update test_msvc.py --- setuptools/tests/test_msvc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index 8c7e17d3..14e0f208 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -73,8 +73,6 @@ class TestModulePatch: mod_name = distutils.msvc9compiler.find_vcvarsall.__module__ assert mod_name == "setuptools.msvc", "find_vcvarsall unpatched" - @pytest.mark.xfail(six.PY2, - reason="https://github.com/pypa/setuptools/issues/707") def test_no_registry_entries_means_nothing_found(self): """ No registry entries or environment variable should lead to an error -- cgit v1.2.1 From de46545db087dc81ea24d2a8695f8fe6ff6a0c78 Mon Sep 17 00:00:00 2001 From: Mehdi Abaakouk Date: Tue, 2 Aug 2016 12:13:41 +0200 Subject: Fix _have_cython In 3c047624, we remove the loop, but we miss to convert the tuple to a string. Because the exception is just ignored, we don't see that __import__ won't works --- setuptools/extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/extension.py b/setuptools/extension.py index 5ea72c06..6b9c19f8 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -19,7 +19,7 @@ def _have_cython(): """ Return True if Cython can be imported. """ - cython_impl = 'Cython.Distutils.build_ext', + cython_impl = 'Cython.Distutils.build_ext' try: # from (cython_impl) import build_ext __import__(cython_impl, fromlist=['build_ext']).build_ext -- cgit v1.2.1 From 18c0fdab26ba13a9cf142408d7f1f2566be21fa7 Mon Sep 17 00:00:00 2001 From: Valentin Valls Date: Wed, 3 Aug 2016 13:55:56 +0200 Subject: Fix logging using arguments instead of formatter --- setuptools/command/bdist_egg.py | 14 +++++++------- setuptools/package_index.py | 3 +-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 50744b87..cbea7537 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -129,7 +129,7 @@ class bdist_egg(Command): self.distribution.data_files.append(item) try: - log.info("installing package data to %s" % self.bdist_dir) + log.info("installing package data to %s", self.bdist_dir) self.call_command('install_data', force=0, root=None) finally: self.distribution.data_files = old @@ -152,7 +152,7 @@ class bdist_egg(Command): self.run_command("egg_info") # We run install_lib before install_data, because some data hacks # pull their data path from the install_lib command. - log.info("installing library code to %s" % self.bdist_dir) + log.info("installing library code to %s", self.bdist_dir) instcmd = self.get_finalized_command('install') old_root = instcmd.root instcmd.root = None @@ -169,7 +169,7 @@ class bdist_egg(Command): pyfile = os.path.join(self.bdist_dir, strip_module(filename) + '.py') self.stubs.append(pyfile) - log.info("creating stub loader for %s" % ext_name) + log.info("creating stub loader for %s", ext_name) if not self.dry_run: write_stub(os.path.basename(ext_name), pyfile) to_compile.append(pyfile) @@ -186,14 +186,14 @@ class bdist_egg(Command): self.mkpath(egg_info) if self.distribution.scripts: script_dir = os.path.join(egg_info, 'scripts') - log.info("installing scripts to %s" % script_dir) + log.info("installing scripts to %s", script_dir) self.call_command('install_scripts', install_dir=script_dir, no_ep=1) self.copy_metadata_to(egg_info) native_libs = os.path.join(egg_info, "native_libs.txt") if all_outputs: - log.info("writing %s" % native_libs) + log.info("writing %s", native_libs) if not self.dry_run: ensure_directory(native_libs) libs_file = open(native_libs, 'wt') @@ -201,7 +201,7 @@ class bdist_egg(Command): libs_file.write('\n') libs_file.close() elif os.path.isfile(native_libs): - log.info("removing %s" % native_libs) + log.info("removing %s", native_libs) if not self.dry_run: os.unlink(native_libs) @@ -458,7 +458,7 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True, p = path[len(base_dir) + 1:] if not dry_run: z.write(path, p) - log.debug("adding '%s'" % p) + log.debug("adding '%s'", p) compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED if not dry_run: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 8d965f49..9ef782cb 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1038,8 +1038,7 @@ def open_with_auth(url, opener=urllib.request.urlopen): cred = PyPIConfig().find_credential(url) if cred: auth = str(cred) - info = cred.username, url - log.info('Authenticating as %s for %s (from .pypirc)' % info) + log.info('Authenticating as %s for %s (from .pypirc)', cred.username, url) if auth: auth = "Basic " + _encode_auth(auth) -- cgit v1.2.1 From 651e3ffacb4208469e97a4eeaba623f5413daa7d Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Wed, 3 Aug 2016 20:15:55 +0200 Subject: Fix from @vallsv Fix from @vallsv --- setuptools/msvc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 785d879b..3e2472a2 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -87,8 +87,8 @@ def patch_for_specialized_compiler(): try: # Patch distutils._msvccompiler.library_dir_option - unpatched['msvc14_library_dir_option'] = msvc14compiler.library_dir_option - msvc14compiler.library_dir_option = msvc14_library_dir_option + unpatched['msvc14_library_dir_option'] = msvc14compiler.MSVCCompiler.library_dir_option + msvc14compiler.MSVCCompiler.library_dir_option = msvc14_library_dir_option except Exception: pass @@ -219,10 +219,10 @@ def msvc14_get_vc_env(plat_spec): raise -def msvc14_library_dir_option(dir): +def msvc14_library_dir_option(self, dir): if ' ' in dir and '"' not in dir: dir = '"%s"' % dir - return unpatched['msvc14_library_dir_option'](dir) + return unpatched['msvc14_library_dir_option'](self, dir) def _augment_exception(exc, version, arch=''): -- cgit v1.2.1 From a2605b297c147a1ad54078181d32fe369fa4f37d Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Wed, 3 Aug 2016 21:55:39 -0400 Subject: use abi3 extension if Extension().is_abi3 --- setuptools/command/build_ext.py | 13 +++++++++++++ setuptools/extension.py | 4 ++++ setuptools/tests/test_build_ext.py | 20 +++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index e6db0764..dad28999 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -59,6 +59,14 @@ elif os.name != 'nt': if_dl = lambda s: s if have_rtld else '' +def get_abi3_suffix(): + """Return the file extension for an abi3-compliant Extension()""" + import imp + for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION): + if '.abi3' in suffix: # Unix + return suffix + elif suffix == '.pyd': # Windows + return suffix class build_ext(_build_ext): @@ -96,6 +104,11 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] + if sys.version_info[0] != 2 and getattr(ext, 'is_abi3'): + from distutils.sysconfig import get_config_var + so_ext = get_config_var('SO') + filename = filename[:-len(so_ext)] + filename = filename + get_abi3_suffix() if isinstance(ext, Library): fn, ext = os.path.splitext(filename) return self.shlib_compiler.library_filename(fn, libtype) diff --git a/setuptools/extension.py b/setuptools/extension.py index 5ea72c06..a6cc0915 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -36,6 +36,10 @@ have_pyrex = _have_cython class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" + def __init__(self, name, sources, is_abi3=False, **kw): + self.is_abi3 = is_abi3 + _Extension.__init__(self, name, sources, **kw) + def _convert_pyx_sources_to_lang(self): """ Replace sources with .pyx extensions to sources with the target diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 5168ebf0..e0f2e73b 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -1,8 +1,10 @@ +import sys import distutils.command.build_ext as orig +from distutils.sysconfig import get_config_var from setuptools.command.build_ext import build_ext from setuptools.dist import Distribution - +from setuptools.extension import Extension class TestBuildExt: @@ -18,3 +20,19 @@ class TestBuildExt: res = cmd.get_ext_filename('foo') wanted = orig.build_ext.get_ext_filename(cmd, 'foo') assert res == wanted + + def test_abi3_filename(self): + """ + Filename needs to be loadable by several versions + of Python 3 if 'is_abi3' is truthy on Extension() + """ + dist = Distribution(dict(ext_modules=[Extension('spam.eggs', [], is_abi3=True)])) + cmd = build_ext(dist) + res = cmd.get_ext_filename('spam.eggs') + + if sys.version_info[0] == 2: + assert res.endswith(get_config_var('SO')) + elif sys.platform == 'win32': + assert res.endswith('eggs.pyd') + else: + assert 'abi3' in res \ No newline at end of file -- cgit v1.2.1 From 5f3dac49084b38a248682475635f866e795f484c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 3 Aug 2016 22:08:08 -0400 Subject: In package_index, reindent long lines. --- setuptools/package_index.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 9ef782cb..82cd608f 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -290,7 +290,12 @@ class PackageIndex(Environment): self.package_pages = {} self.allows = re.compile('|'.join(map(translate, hosts))).match self.to_scan = [] - if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()): + use_ssl = ( + verify_ssl + and ssl_support.is_available + and (ca_bundle or ssl_support.find_ca_bundle()) + ) + if use_ssl: self.opener = ssl_support.opener_for(ca_bundle) else: self.opener = urllib.request.urlopen @@ -320,7 +325,8 @@ class PackageIndex(Environment): self.info("Reading %s", url) self.fetched_urls[url] = True # prevent multiple fetch attempts - f = self.open_url(url, "Download error on %s: %%s -- Some packages may not be found!" % url) + tmpl = "Download error on %s: %%s -- Some packages may not be found!" + f = self.open_url(url, tmpl % url) if f is None: return self.fetched_urls[f.url] = True @@ -362,7 +368,8 @@ class PackageIndex(Environment): def url_ok(self, url, fatal=False): s = URL_SCHEME(url) - if (s and s.group(1).lower() == 'file') or self.allows(urllib.parse.urlparse(url)[1]): + is_file = s and s.group(1).lower() == 'file' + if is_file or self.allows(urllib.parse.urlparse(url)[1]): return True msg = ("\nNote: Bypassing %s (disallowed host; see " "http://bit.ly/1dg9ijs for details).\n") @@ -1038,7 +1045,8 @@ def open_with_auth(url, opener=urllib.request.urlopen): cred = PyPIConfig().find_credential(url) if cred: auth = str(cred) - log.info('Authenticating as %s for %s (from .pypirc)', cred.username, url) + info = cred.username, url + log.info('Authenticating as %s for %s (from .pypirc)', *info) if auth: auth = "Basic " + _encode_auth(auth) -- cgit v1.2.1 From 7b1fa7643e2599f24956323a2066a6e26dc57b82 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Thu, 4 Aug 2016 12:22:54 +0200 Subject: doc for msvc14_library_dir_option --- setuptools/msvc.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 3e2472a2..da26371c 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -86,7 +86,7 @@ def patch_for_specialized_compiler(): pass try: - # Patch distutils._msvccompiler.library_dir_option + # Patch distutils._msvccompiler.MSVCCompiler.library_dir_option unpatched['msvc14_library_dir_option'] = msvc14compiler.MSVCCompiler.library_dir_option msvc14compiler.MSVCCompiler.library_dir_option = msvc14_library_dir_option except Exception: @@ -220,7 +220,21 @@ def msvc14_get_vc_env(plat_spec): def msvc14_library_dir_option(self, dir): + """ + Patched "distutils._msvccompiler.MSVCCompiler.library_dir_option" + to fix unquoted path in "\LIBPATH" argument when a space is on path. + + Parameters + ---------- + dir: str + Path to convert in "\LIBPATH" argument. + + Return + ------ + "\LIBPATH" argument: str + """ if ' ' in dir and '"' not in dir: + # Quote if space and not already quoted dir = '"%s"' % dir return unpatched['msvc14_library_dir_option'](self, dir) -- cgit v1.2.1 From a60816441e9b185a09f1dacdb2d5612d9a324787 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Aug 2016 07:11:07 -0400 Subject: Update changelog --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5a2b9928..eda52476 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v25.1.4 +------- + +* #717 +* #713 +* #707 via #715 + v25.1.3 ------- -- cgit v1.2.1 From b8bb34bbe89baec3dee225eeb7e17951578f5368 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Aug 2016 07:11:14 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.3=20=E2=86=92=2025.1.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 4f08b809..ae67632a 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.3 +current_version = 25.1.4 commit = True tag = True diff --git a/setup.py b/setup.py index 2309b7d2..d7933ab3 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.3", + version="25.1.4", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From eb4a2e50b292fc9ce1cc2a55bf3a18fc303b4c24 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Aug 2016 07:11:15 -0400 Subject: Added tag v25.1.4 for changeset 6f55250b9c58 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 1b833185..9eb59386 100644 --- a/.hgtags +++ b/.hgtags @@ -297,3 +297,4 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 1cbb29c235439331a76c7b6b5cf8701f763478d3 v25.1.2 c350190e7bbf274e6728f14af7451b1fd3aaeba2 25.1.2 86e668badaf45315bb8506ac2312665d129a0322 v25.1.3 +6f55250b9c5856557ac669d1f966bba8be9eb1d2 v25.1.4 -- cgit v1.2.1 From aa835dcab3b98944c928a73eab8b091479514155 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Aug 2016 13:13:37 -0400 Subject: Collapse two functions into one. --- pkg_resources/__init__.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 9f0ea6ec..2a053b50 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1468,16 +1468,11 @@ class NullProvider: def has_metadata(self, name): return self.egg_info and self._has(self._fn(self.egg_info, name)) - if sys.version_info <= (3,): - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info, name)) - else: - def get_metadata(self, name): - if not self.egg_info: - return "" - return self._get(self._fn(self.egg_info, name)).decode("utf-8") + def get_metadata(self, name): + if not self.egg_info: + return "" + value = self._get(self._fn(self.egg_info, name)) + return value.decode('utf-8') if six.PY3 else value def get_metadata_lines(self, name): return yield_lines(self.get_metadata(name)) -- cgit v1.2.1 From 21efdb5615f98a60cfa0b6130a1c2d93e5faa28e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Aug 2016 13:14:45 -0400 Subject: Allow an environment to suppress errors when reading metadata by setting PKG_RESOURCES_METADATA_ERRORS='replace'. Ref #719. --- pkg_resources/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2a053b50..b402ddd6 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1859,7 +1859,9 @@ class FileMetadata(EmptyProvider): def get_metadata(self, name): if name=='PKG-INFO': - with io.open(self.path, encoding='utf-8') as f: + env_key = 'PKG_RESOURCES_METADATA_ERRORS' + errors = os.environ.get(env_key, 'strict') + with io.open(self.path, encoding='utf-8', errors=errors) as f: try: metadata = f.read() except UnicodeDecodeError as exc: -- cgit v1.2.1 From dcf1cef2ed47f923c873cef9ea42727cfb819c7d Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Thu, 4 Aug 2016 18:46:15 -0400 Subject: setup.py: Bump `certifi` to version `2016.8.2`. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index d7933ab3..ca3cf405 100755 --- a/setup.py +++ b/setup.py @@ -167,11 +167,11 @@ setup_params = dict( """).strip().splitlines(), extras_require={ "ssl:sys_platform=='win32'": "wincertstore==0.2", - "certs": "certifi==2016.2.28", + "certs": "certifi==2016.8.2", }, dependency_links=[ pypi_link( - 'certifi-2016.2.28.tar.gz#md5=5d672aa766e1f773c75cfeccd02d3650', + 'certifi-2016.8.2.tar.gz#md5=004ae166985d3a684bcac5368e22ed63', ), pypi_link( 'wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2', -- cgit v1.2.1 From e3805614a5ed770d3ba86acd339a24947c19a65b Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 5 Aug 2016 12:58:47 +0200 Subject: quote library_dir_option after calling unpatched version avoids double-quotes if the calling function does the quoting correctly. --- setuptools/msvc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 2700a2b0..97dd441c 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -233,10 +233,11 @@ def msvc14_library_dir_option(self, dir): ------ "\LIBPATH" argument: str """ - if ' ' in dir and '"' not in dir: + opt = unpatched['msvc14_library_dir_option'](self, dir) + if ' ' in opt and '"' not in opt: # Quote if space and not already quoted - dir = '"%s"' % dir - return unpatched['msvc14_library_dir_option'](self, dir) + opt = '"%s"' % opt + return opt def _augment_exception(exc, version, arch=''): -- cgit v1.2.1 From dab253cb72eb7c098393f985e32585e079607f66 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 5 Aug 2016 07:45:09 -0400 Subject: call finalize_options in test --- setuptools/tests/test_build_ext.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index e0f2e73b..c71aadca 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -2,7 +2,7 @@ import sys import distutils.command.build_ext as orig from distutils.sysconfig import get_config_var -from setuptools.command.build_ext import build_ext +from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution from setuptools.extension import Extension @@ -26,8 +26,13 @@ class TestBuildExt: Filename needs to be loadable by several versions of Python 3 if 'is_abi3' is truthy on Extension() """ - dist = Distribution(dict(ext_modules=[Extension('spam.eggs', [], is_abi3=True)])) + print(get_abi3_suffix()) + + extension = Extension('spam.eggs', ['eggs.c'], is_abi3=True) + dist = Distribution(dict(ext_modules=[extension])) cmd = build_ext(dist) + cmd.finalize_options() + assert 'spam.eggs' in cmd.ext_map res = cmd.get_ext_filename('spam.eggs') if sys.version_info[0] == 2: -- cgit v1.2.1 From 702a4277768f0781e3d0a4cf770d29621f7f2cc3 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 5 Aug 2016 07:55:57 -0400 Subject: rename is_abi3 to py_limited_api --- setuptools/command/build_ext.py | 4 +++- setuptools/extension.py | 4 ++-- setuptools/tests/test_build_ext.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index dad28999..7bb4d24c 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -104,7 +104,9 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] - if sys.version_info[0] != 2 and getattr(ext, 'is_abi3'): + if (sys.version_info[0] != 2 + and getattr(ext, 'py_limited_api') + and get_abi3_suffix()): from distutils.sysconfig import get_config_var so_ext = get_config_var('SO') filename = filename[:-len(so_ext)] diff --git a/setuptools/extension.py b/setuptools/extension.py index a6cc0915..c8e9bc7d 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -36,8 +36,8 @@ have_pyrex = _have_cython class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" - def __init__(self, name, sources, is_abi3=False, **kw): - self.is_abi3 = is_abi3 + def __init__(self, name, sources, py_limited_api=False, **kw): + self.py_limited_api = py_limited_api _Extension.__init__(self, name, sources, **kw) def _convert_pyx_sources_to_lang(self): diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index c71aadca..100869f6 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -28,7 +28,7 @@ class TestBuildExt: """ print(get_abi3_suffix()) - extension = Extension('spam.eggs', ['eggs.c'], is_abi3=True) + extension = Extension('spam.eggs', ['eggs.c'], py_limited_api=True) dist = Distribution(dict(ext_modules=[extension])) cmd = build_ext(dist) cmd.finalize_options() -- cgit v1.2.1 From 0a3f850b3bc6c888f0f8810e453e664f9fd320f6 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 5 Aug 2016 08:01:58 -0400 Subject: gate test against get_abi3_suffix() --- setuptools/tests/test_build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 100869f6..f2e1f59d 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -35,7 +35,7 @@ class TestBuildExt: assert 'spam.eggs' in cmd.ext_map res = cmd.get_ext_filename('spam.eggs') - if sys.version_info[0] == 2: + if sys.version_info[0] == 2 or not get_abi3_suffix(): assert res.endswith(get_config_var('SO')) elif sys.platform == 'win32': assert res.endswith('eggs.pyd') -- cgit v1.2.1 From 9d60e5d491298f36a5de33c60c462d1a900844e8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 10:30:46 -0400 Subject: Forget the environment variable, and just log a warning when a metadata can't be decoded. Ref #719. --- pkg_resources/__init__.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index b402ddd6..7dbd13a6 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1,3 +1,5 @@ +# coding: utf-8 + """ Package resource API -------------------- @@ -1859,19 +1861,19 @@ class FileMetadata(EmptyProvider): def get_metadata(self, name): if name=='PKG-INFO': - env_key = 'PKG_RESOURCES_METADATA_ERRORS' - errors = os.environ.get(env_key, 'strict') - with io.open(self.path, encoding='utf-8', errors=errors) as f: - try: - metadata = f.read() - except UnicodeDecodeError as exc: - # add path context to error message - tmpl = " in {self.path}" - exc.reason += tmpl.format(self=self) - raise + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) return metadata raise KeyError("No metadata except PKG-INFO is available") + def _warn_on_replacement(self, metadata): + replacement_char = '�' + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + def get_metadata_lines(self, name): return yield_lines(self.get_metadata(name)) -- cgit v1.2.1 From 09cbf5e63f5c9e84438cf69711a7a6b767106506 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 10:41:12 -0400 Subject: Restore Python 2 compatibility. Ref #719. --- pkg_resources/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 7dbd13a6..94afaaf7 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1868,7 +1868,8 @@ class FileMetadata(EmptyProvider): raise KeyError("No metadata except PKG-INFO is available") def _warn_on_replacement(self, metadata): - replacement_char = '�' + # Python 2.6 and 3.2 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') if replacement_char in metadata: tmpl = "{self.path} could not be properly decoded in UTF-8" msg = tmpl.format(**locals()) -- cgit v1.2.1 From cadadb03546aced58233be95683a2674627ca899 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 10:43:58 -0400 Subject: Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index eda52476..07f0d191 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v25.1.5 +------- + +* #720 +* #723 + v25.1.4 ------- -- cgit v1.2.1 From cf36cf1e47f4a4dda14297ce8612d19faade4ccb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 11:09:33 -0400 Subject: Correct tag for v25.1.2 --- .hgtags | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.hgtags b/.hgtags index 9eb59386..4eaae61d 100644 --- a/.hgtags +++ b/.hgtags @@ -295,6 +295,6 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 529a76a860c50d3cc262759b5b9ce28f171236f9 v25.1.0 392ee093902e14a1d2a6eefc389a7b9ac78b3f9e v25.1.1 1cbb29c235439331a76c7b6b5cf8701f763478d3 v25.1.2 -c350190e7bbf274e6728f14af7451b1fd3aaeba2 25.1.2 +c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 86e668badaf45315bb8506ac2312665d129a0322 v25.1.3 6f55250b9c5856557ac669d1f966bba8be9eb1d2 v25.1.4 -- cgit v1.2.1 From 0fc25ba55daa069374812e33897c712553fdc3f4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 11:12:50 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.4=20=E2=86=92=2025.1.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index ae67632a..3fad1dbc 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.4 +current_version = 25.1.5 commit = True tag = True diff --git a/setup.py b/setup.py index ca3cf405..67891be2 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.4", + version="25.1.5", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 409e1383315ef3f5f2f074b5d95a0bd9c2d66cdf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 11:12:51 -0400 Subject: Added tag v25.1.5 for changeset 76143bb477b5 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 4eaae61d..bff7e77e 100644 --- a/.hgtags +++ b/.hgtags @@ -298,3 +298,4 @@ dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 86e668badaf45315bb8506ac2312665d129a0322 v25.1.3 6f55250b9c5856557ac669d1f966bba8be9eb1d2 v25.1.4 +76143bb477b50314ab6f4ccc4ced80ee43f0dc94 v25.1.5 -- cgit v1.2.1 From a5791ac002d9d00759b63727f9c930a2acba7d9f Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Fri, 5 Aug 2016 17:44:26 +0200 Subject: revert `library_dir_option` patch. Revert patch on `distutils._msvccompiler.MSVCCompiler.library_dir_option` See comments on #694. --- setuptools/msvc.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 97dd441c..4616d4be 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -85,13 +85,6 @@ def patch_for_specialized_compiler(): except Exception: pass - try: - # Patch distutils._msvccompiler.MSVCCompiler.library_dir_option - unpatched['msvc14_library_dir_option'] = msvc14compiler.MSVCCompiler.library_dir_option - msvc14compiler.MSVCCompiler.library_dir_option = msvc14_library_dir_option - except Exception: - pass - def msvc9_find_vcvarsall(version): """ @@ -219,27 +212,6 @@ def msvc14_get_vc_env(plat_spec): raise -def msvc14_library_dir_option(self, dir): - """ - Patched "distutils._msvccompiler.MSVCCompiler.library_dir_option" - to fix unquoted path in "\LIBPATH" argument when a space is on path. - - Parameters - ---------- - dir: str - Path to convert in "\LIBPATH" argument. - - Return - ------ - "\LIBPATH" argument: str - """ - opt = unpatched['msvc14_library_dir_option'](self, dir) - if ' ' in opt and '"' not in opt: - # Quote if space and not already quoted - opt = '"%s"' % opt - return opt - - def _augment_exception(exc, version, arch=''): """ Add details to the exception message to help guide the user -- cgit v1.2.1 From 79d4a0c6ef679e0b29e00ed854d06deb355c89e8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 12:07:32 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 07f0d191..3077f39d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v25.1.6 +------- + +* #725 + v25.1.5 ------- -- cgit v1.2.1 From 38fce51e389f9e7872f4e8062f854e442ef4d75f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 12:07:38 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.5=20=E2=86=92=2025.1.6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 3fad1dbc..ae2125f8 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.5 +current_version = 25.1.6 commit = True tag = True diff --git a/setup.py b/setup.py index 67891be2..d0c50bd6 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.5", + version="25.1.6", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 1b2e94943653960347936bba0eff54cb8dbfc638 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Aug 2016 12:07:39 -0400 Subject: Added tag v25.1.6 for changeset 2db4c66aeae4 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index bff7e77e..19ccb0e4 100644 --- a/.hgtags +++ b/.hgtags @@ -299,3 +299,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 86e668badaf45315bb8506ac2312665d129a0322 v25.1.3 6f55250b9c5856557ac669d1f966bba8be9eb1d2 v25.1.4 76143bb477b50314ab6f4ccc4ced80ee43f0dc94 v25.1.5 +2db4c66aeae47217aaf92099a9875e9e810c9cbb v25.1.6 -- cgit v1.2.1 From 29c8385814335e91538d76b68578b6c0ab483c1a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 8 Aug 2016 14:39:07 -0400 Subject: Update upload target --- .travis.yml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 32b664d8..ed077d94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ before_deploy: deploy: provider: pypi # Also update server in setup.cfg - server: https://upload.pypi.io/legacy/ + server: https://upload.pypi.org/legacy/ on: tags: true all_branches: true diff --git a/setup.cfg b/setup.cfg index ae2125f8..77b731d9 100755 --- a/setup.cfg +++ b/setup.cfg @@ -15,7 +15,7 @@ binary = bdist_egg upload --show-response test = pytest [upload] -repository = https://upload.pypi.io/legacy/ +repository = https://upload.pypi.org/legacy/ [sdist] formats = gztar zip -- cgit v1.2.1 From 838395475ceafc0287b49b1b6d00cd589417acb0 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Fri, 12 Aug 2016 07:44:46 -0500 Subject: Add LICENSE file for expectant auditors The classifier in setup.py should be enough for most auditors, but some have automated tooling that checks for LICENSE files and reads those. This helps all auditors (downstream redistributors and users) who can not just rely on the classifier. Related to #612 --- CHANGES.rst | 6 ++++++ LICENSE | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 LICENSE diff --git a/CHANGES.rst b/CHANGES.rst index 3077f39d..f5205339 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v25.2.0 +------- + +* #612 via #730: Add a LICENSE file which needs to be provided by the terms of + the MIT license. + v25.1.6 ------- diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..6e0693b4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2016 Jason R Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -- cgit v1.2.1 From 043bf1e726b8a59f88c6d3120c350b9d31677876 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 12 Aug 2016 15:05:16 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.1.6=20=E2=86=92=2025.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 77b731d9..92fd8354 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.1.6 +current_version = 25.2.0 commit = True tag = True diff --git a/setup.py b/setup.py index d0c50bd6..fa82f360 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.1.6", + version="25.2.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From c5d5d63262ad9e44b6d51a0b486c7d288c65b2ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 12 Aug 2016 15:05:17 -0400 Subject: Added tag v25.2.0 for changeset 2083d7c3fadc --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 19ccb0e4..75a9fb08 100644 --- a/.hgtags +++ b/.hgtags @@ -300,3 +300,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 6f55250b9c5856557ac669d1f966bba8be9eb1d2 v25.1.4 76143bb477b50314ab6f4ccc4ced80ee43f0dc94 v25.1.5 2db4c66aeae47217aaf92099a9875e9e810c9cbb v25.1.6 +2083d7c3fadcf15b3bc07f7532440efbcf8fd18d v25.2.0 -- cgit v1.2.1 From c3b6f615a61533779754c014b71f4db4761574fa Mon Sep 17 00:00:00 2001 From: Gabi Davar Date: Sat, 13 Aug 2016 10:59:01 +0300 Subject: bump certifi to 2016.8.8 --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index fa82f360..5fa237d5 100755 --- a/setup.py +++ b/setup.py @@ -167,11 +167,11 @@ setup_params = dict( """).strip().splitlines(), extras_require={ "ssl:sys_platform=='win32'": "wincertstore==0.2", - "certs": "certifi==2016.8.2", + "certs": "certifi==2016.8.8", }, dependency_links=[ pypi_link( - 'certifi-2016.8.2.tar.gz#md5=004ae166985d3a684bcac5368e22ed63', + 'certifi-2016.8.8.tar.gz#md5=b57513f7670482da45bb350b792f659e', ), pypi_link( 'wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2', -- cgit v1.2.1 From 7c7a24d130ee00344a41115cbacd1b0d2147d983 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 13 Aug 2016 11:22:08 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f5205339..bffad4c5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v25.3.0 +------- + +#731: Bump certifi. + v25.2.0 ------- -- cgit v1.2.1 From b432bcae27cadc91a7df4ee4851a41ccb0674bf9 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Sun, 14 Aug 2016 08:06:59 +0200 Subject: Update tox.ini envlist to match supported Pythons Pythons 3.1, 3.2 are no longer supported, but Python 3.5, Pypy, and Pypy3 are. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 9061869f..8c09e7df 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,py31,py32,py33,py34 +envlist = py26,py27,py33,py34,py35,pypy,pypy3 [testenv] commands=python setup.py test -- cgit v1.2.1 From f47bc22fc59d6262ece42722e3a502c672f0558f Mon Sep 17 00:00:00 2001 From: John Kirkham Date: Sun, 14 Aug 2016 20:40:18 -0400 Subject: Include the license in sdists and similar packagings. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index cfd1b001..e25a5ea5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,7 @@ recursive-include pkg_resources *.py *.txt include *.py include *.rst include MANIFEST.in +include LICENSE include launcher.c include msvc-build-launcher.cmd include pytest.ini -- cgit v1.2.1 From 853a9df48cc056a07e17511a2b65918af9605bbc Mon Sep 17 00:00:00 2001 From: Ofekmeister Date: Tue, 16 Aug 2016 02:12:38 -0400 Subject: Fix issue #459 Patched sys.argv[0] before loading entry point --- setuptools/command/easy_install.py | 2 ++ setuptools/tests/test_easy_install.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index ae9079d8..e2a7dc46 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2022,6 +2022,8 @@ class ScriptWriter(object): from pkg_resources import load_entry_point if __name__ == '__main__': + import re + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit( load_entry_point(%(spec)r, %(group)r, %(name)r)() ) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index da0a355c..a4b1dfd5 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -78,6 +78,8 @@ class TestEasyInstallTest: from pkg_resources import load_entry_point if __name__ == '__main__': + import re + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit( load_entry_point('spec', 'console_scripts', 'name')() ) -- cgit v1.2.1 From 5e1693d225b2416712e11da591f60c085ce62957 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 16 Aug 2016 12:43:16 +0200 Subject: numpy.distutils and distutils._msvccompiler compatibility - Fix compatibility between `numpy.distutils` and `distutils._msvccompiler`. See #728 : Setuptools 24 `msvc.py` improvement import `distutils._msvccompiler` (New Python 3.5 C compiler for MSVC >= 14), but this one is not compatible with `numpy.distutils` (because not patched with `numpy.distutils.ccompiler.gen_lib_options`) and return unquoted libpaths when linking. The problem was patched in Numpy, but need to be patched also in Setuptools for compatibility between older versions of Numpy and `distutils._msvccompiler` (and indirectly Setuptools > 24). - Replace some residuals `except Exception`. --- setuptools/msvc.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 4616d4be..bd486fa1 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -75,14 +75,23 @@ def patch_for_specialized_compiler(): msvc9compiler.find_vcvarsall = msvc9_find_vcvarsall unpatched['msvc9_query_vcvarsall'] = msvc9compiler.query_vcvarsall msvc9compiler.query_vcvarsall = msvc9_query_vcvarsall - except Exception: + 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 Exception: + except NameError: + pass + + try: + # Apply "gen_lib_options" patch from Numpy to "distutils._msvccompiler" + # to fix compatibility between "numpy.distutils" and + # "distutils._msvccompiler" (for Numpy < 1.11.2) + import numpy.distutils as np_distutils + msvc14compiler.gen_lib_options = np_distutils.ccompiler.gen_lib_options + except (ImportError, NameError): pass @@ -243,7 +252,8 @@ def _augment_exception(exc, version, arch=''): elif version >= 14.0: # For VC++ 14.0 Redirect user to Visual C++ Build Tools message += (' Get it with "Microsoft Visual C++ Build Tools": ' - r'http://landinghub.visualstudio.com/visual-cpp-build-tools') + r'http://landinghub.visualstudio.com/' + 'visual-cpp-build-tools') exc.args = (message, ) -- cgit v1.2.1 From 4c2b0a2d9c19218086b5a6ecb837403bd5bb8135 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 16 Aug 2016 12:55:19 +0200 Subject: Add history for unquoted libpaths problem --- CHANGES.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bffad4c5..9f098df9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v25.3.1 +------- + +* #739 Fix unquoted libpaths by fixing compatibility between `numpy.distutils` and `distutils._msvccompiler` for numpy < 1.11.2 (Fix issue #728, error also fixed in Numpy). + v25.3.0 ------- @@ -16,20 +21,21 @@ v25.2.0 v25.1.6 ------- -* #725 +* #725: revert `library_dir_option` patch (Error is related to `numpy.distutils` and make errors on non Numpy users). v25.1.5 ------- * #720 -* #723 +* #723: Improve patch for `library_dir_option`. v25.1.4 ------- * #717 * #713 -* #707 via #715 +* #707: Fix Python 2 compatibility for MSVC by catching errors properly. +* #715: Fix unquoted libpaths by patching `library_dir_option`. v25.1.3 ------- -- cgit v1.2.1 From 38d6743427f0aa31eaf2eb07df5cd11b6526d036 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 16 Aug 2016 19:12:42 +0200 Subject: Patch with numpy at execution time and not at import time --- setuptools/msvc.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index bd486fa1..4646d83f 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -79,19 +79,17 @@ def patch_for_specialized_compiler(): pass try: - # Patch distutils._msvccompiler._get_vc_env + # Patch distutils._msvccompiler._get_vc_env for numpy compatibility unpatched['msvc14_get_vc_env'] = msvc14compiler._get_vc_env msvc14compiler._get_vc_env = msvc14_get_vc_env except NameError: pass try: - # Apply "gen_lib_options" patch from Numpy to "distutils._msvccompiler" - # to fix compatibility between "numpy.distutils" and - # "distutils._msvccompiler" (for Numpy < 1.11.2) - import numpy.distutils as np_distutils - msvc14compiler.gen_lib_options = np_distutils.ccompiler.gen_lib_options - except (ImportError, NameError): + # Patch distutils._msvccompiler.gen_lib_options + unpatched['msvc14_gen_lib_options'] = msvc14compiler.gen_lib_options + msvc14compiler.gen_lib_options = msvc14_gen_lib_options + except NameError: pass @@ -221,6 +219,19 @@ def msvc14_get_vc_env(plat_spec): raise +def msvc14_gen_lib_options(*args, **kwargs): + """ + Patched "distutils._msvccompiler.gen_lib_options" for fix + compatibility between "numpy.distutils" and "distutils._msvccompiler" + (for Numpy < 1.11.2) + """ + if "numpy" in distutils.ccompiler.CCompiler.spawn.__module__: + import numpy as np + return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) + else: + return unpatched['msvc14_gen_lib_options'](*args, **kwargs) + + def _augment_exception(exc, version, arch=''): """ Add details to the exception message to help guide the user -- cgit v1.2.1 From f5802f369d5b7b76a7feb4c49e2e49840058bf3b Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 16 Aug 2016 19:33:07 +0200 Subject: Improve numpy.distutils detection Agree with @pitrou comment. --- setuptools/msvc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 4646d83f..57153d8a 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -2,6 +2,7 @@ This module adds improved support for Microsoft Visual C++ compilers. """ import os +import sys import platform import itertools import distutils.errors @@ -225,7 +226,7 @@ def msvc14_gen_lib_options(*args, **kwargs): compatibility between "numpy.distutils" and "distutils._msvccompiler" (for Numpy < 1.11.2) """ - if "numpy" in distutils.ccompiler.CCompiler.spawn.__module__: + if "numpy.distutils" in sys.modules: import numpy as np return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) else: -- cgit v1.2.1 From 21be70b60cfea8da91df4687a21f262a59809073 Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Tue, 16 Aug 2016 19:34:48 +0200 Subject: Wrong line for comment --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 57153d8a..360c1a68 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -80,14 +80,14 @@ def patch_for_specialized_compiler(): pass try: - # Patch distutils._msvccompiler._get_vc_env for numpy compatibility + # 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 + # 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: -- cgit v1.2.1 From fadf148e520e84cdd23856457040799dfe9fa592 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 16 Aug 2016 15:59:17 +1000 Subject: Add tests for MANIFEST.in --- setuptools/tests/test_manifest.py | 210 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 setuptools/tests/test_manifest.py diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py new file mode 100644 index 00000000..abee5128 --- /dev/null +++ b/setuptools/tests/test_manifest.py @@ -0,0 +1,210 @@ +# -*- coding: utf-8 -*- +"""sdist tests""" + +import contextlib +import os +import shutil +import sys +import tempfile + +from setuptools.command.egg_info import egg_info +from setuptools.dist import Distribution +from setuptools.extern import six +from setuptools.tests.textwrap import DALS + +import pytest + +py3_only = pytest.mark.xfail(six.PY2, reason="Test runs on Python 3 only") + + +def make_local_path(s): + """Converts '/' in a string to os.sep""" + return s.replace('/', os.sep) + + +SETUP_ATTRS = { + 'name': 'app', + 'version': '0.0', + 'packages': ['app'], +} + + +SETUP_PY = """\ +from setuptools import setup + +setup(**%r) +""" % SETUP_ATTRS + + +@contextlib.contextmanager +def quiet(): + old_stdout, old_stderr = sys.stdout, sys.stderr + sys.stdout, sys.stderr = six.StringIO(), six.StringIO() + try: + yield + finally: + sys.stdout, sys.stderr = old_stdout, old_stderr + + +def touch(filename): + open(filename, 'w').close() + +# The set of files always in the manifest, including all files in the +# .egg-info directory +default_files = frozenset(map(make_local_path, [ + 'README.rst', + 'MANIFEST.in', + 'setup.py', + 'app.egg-info/PKG-INFO', + 'app.egg-info/SOURCES.txt', + 'app.egg-info/dependency_links.txt', + 'app.egg-info/top_level.txt', + 'app/__init__.py', +])) + + +class TestManifestTest: + + def setup_method(self, method): + self.temp_dir = tempfile.mkdtemp() + f = open(os.path.join(self.temp_dir, 'setup.py'), 'w') + f.write(SETUP_PY) + f.close() + + """ + Create a file tree like: + - LICENSE + - README.rst + - testing.rst + - .hidden.rst + - app/ + - __init__.py + - a.txt + - b.txt + - c.rst + - static/ + - app.js + - app.js.map + - app.css + - app.css.map + """ + + for fname in ['README.rst', '.hidden.rst', 'testing.rst', 'LICENSE']: + touch(os.path.join(self.temp_dir, fname)) + + # Set up the rest of the test package + test_pkg = os.path.join(self.temp_dir, 'app') + os.mkdir(test_pkg) + for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']: + touch(os.path.join(test_pkg, fname)) + + # Some compiled front-end assets to include + static = os.path.join(test_pkg, 'static') + os.mkdir(static) + for fname in ['app.js', 'app.js.map', 'app.css', 'app.css.map']: + touch(os.path.join(static, fname)) + + self.old_cwd = os.getcwd() + os.chdir(self.temp_dir) + + def teardown_method(self, method): + os.chdir(self.old_cwd) + shutil.rmtree(self.temp_dir) + + def make_manifest(self, contents): + """Write a MANIFEST.in.""" + with open(os.path.join(self.temp_dir, 'MANIFEST.in'), 'w') as f: + f.write(DALS(contents)) + + def get_files(self): + """Run egg_info and get all the files to include, as a set""" + dist = Distribution(SETUP_ATTRS) + dist.script_name = 'setup.py' + cmd = egg_info(dist) + cmd.ensure_finalized() + + cmd.run() + + return set(cmd.filelist.files) + + def test_no_manifest(self): + """Check a missing MANIFEST.in includes only the standard files.""" + assert (default_files - set(['MANIFEST.in'])) == self.get_files() + + def test_empty_files(self): + """Check an empty MANIFEST.in includes only the standard files.""" + self.make_manifest("") + assert default_files == self.get_files() + + def test_include(self): + """Include extra rst files in the project root.""" + self.make_manifest("include *.rst") + files = default_files | set([ + 'testing.rst', '.hidden.rst']) + assert files == self.get_files() + + def test_exclude(self): + """Include everything in app/ except the text files""" + l = make_local_path + self.make_manifest( + """ + include app/* + exclude app/*.txt + """) + files = default_files | set([l('app/c.rst')]) + assert files == self.get_files() + + def test_include_multiple(self): + """Include with multiple patterns.""" + l = make_local_path + self.make_manifest("include app/*.txt app/static/*") + files = default_files | set([ + l('app/a.txt'), l('app/b.txt'), + l('app/static/app.js'), l('app/static/app.js.map'), + l('app/static/app.css'), l('app/static/app.css.map')]) + assert files == self.get_files() + + def test_graft(self): + """Include the whole app/static/ directory.""" + l = make_local_path + self.make_manifest("graft app/static") + files = default_files | set([ + l('app/static/app.js'), l('app/static/app.js.map'), + l('app/static/app.css'), l('app/static/app.css.map')]) + assert files == self.get_files() + + def test_graft_global_exclude(self): + """Exclude all *.map files in the project.""" + l = make_local_path + self.make_manifest( + """ + graft app/static + global-exclude *.map + """) + files = default_files | set([ + l('app/static/app.js'), l('app/static/app.css')]) + assert files == self.get_files() + + def test_global_include(self): + """Include all *.rst, *.js, and *.css files in the whole tree.""" + l = make_local_path + self.make_manifest( + """ + global-include *.rst *.js *.css + """) + files = default_files | set([ + '.hidden.rst', 'testing.rst', l('app/c.rst'), + l('app/static/app.js'), l('app/static/app.css')]) + assert files == self.get_files() + + def test_graft_prune(self): + """Include all files in app/, except for the whole app/static/ dir.""" + l = make_local_path + self.make_manifest( + """ + graft app + prune app/static + """) + files = default_files | set([ + l('app/a.txt'), l('app/b.txt'), l('app/c.rst')]) + assert files == self.get_files() -- cgit v1.2.1 From 498f86b8979b644234e8cfebfa990385504e926f Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Tue, 16 Aug 2016 16:36:58 +1000 Subject: Copy FileList tests from distutils --- setuptools/tests/test_manifest.py | 283 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 274 insertions(+), 9 deletions(-) diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index abee5128..6e67ca61 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -6,8 +6,10 @@ import os import shutil import sys import tempfile +from distutils import log +from distutils.errors import DistutilsTemplateError -from setuptools.command.egg_info import egg_info +from setuptools.command.egg_info import FileList, egg_info from setuptools.dist import Distribution from setuptools.extern import six from setuptools.tests.textwrap import DALS @@ -63,10 +65,23 @@ default_files = frozenset(map(make_local_path, [ ])) -class TestManifestTest: +class TempDirTestCase(object): def setup_method(self, method): self.temp_dir = tempfile.mkdtemp() + self.old_cwd = os.getcwd() + os.chdir(self.temp_dir) + + def teardown_method(self, method): + os.chdir(self.old_cwd) + shutil.rmtree(self.temp_dir) + + +class TestManifestTest(TempDirTestCase): + + def setup_method(self, method): + super(TestManifestTest, self).setup_method(method) + f = open(os.path.join(self.temp_dir, 'setup.py'), 'w') f.write(SETUP_PY) f.close() @@ -104,13 +119,6 @@ class TestManifestTest: for fname in ['app.js', 'app.js.map', 'app.css', 'app.css.map']: touch(os.path.join(static, fname)) - self.old_cwd = os.getcwd() - os.chdir(self.temp_dir) - - def teardown_method(self, method): - os.chdir(self.old_cwd) - shutil.rmtree(self.temp_dir) - def make_manifest(self, contents): """Write a MANIFEST.in.""" with open(os.path.join(self.temp_dir, 'MANIFEST.in'), 'w') as f: @@ -208,3 +216,260 @@ class TestManifestTest: files = default_files | set([ l('app/a.txt'), l('app/b.txt'), l('app/c.rst')]) assert files == self.get_files() + + +class TestFileListTest(TempDirTestCase): + """ + A copy of the relevant bits of distutils/tests/test_filelist.py, + to ensure setuptools' version of FileList keeps parity with distutils. + """ + + def setup_method(self, method): + super(TestFileListTest, self).setup_method(method) + self.threshold = log.set_threshold(log.FATAL) + self._old_log = log.Log._log + log.Log._log = self._log + self.logs = [] + + def teardown_method(self, method): + log.set_threshold(self.threshold) + log.Log._log = self._old_log + super(TestFileListTest, self).teardown_method(method) + + def _log(self, level, msg, args): + if level not in (log.DEBUG, log.INFO, log.WARN, log.ERROR, log.FATAL): + raise ValueError('%s wrong log level' % str(level)) + self.logs.append((level, msg, args)) + + def get_logs(self, *levels): + def _format(msg, args): + if len(args) == 0: + return msg + return msg % args + return [_format(msg, args) for level, msg, args + in self.logs if level in levels] + + def clear_logs(self): + self.logs = [] + + def assertNoWarnings(self): + assert self.get_logs(log.WARN) == [] + self.clear_logs() + + def assertWarnings(self): + assert len(self.get_logs(log.WARN)) > 0 + self.clear_logs() + + def make_files(self, files): + for file in files: + file = os.path.join(self.temp_dir, file) + dirname, basename = os.path.split(file) + if not os.path.exists(dirname): + os.makedirs(dirname) + open(file, 'w').close() + + def test_process_template_line(self): + # testing all MANIFEST.in template patterns + file_list = FileList() + l = make_local_path + + # simulated file list + self.make_files([ + 'foo.tmp', 'ok', 'xo', 'four.txt', + 'buildout.cfg', + # filelist does not filter out VCS directories, + # it's sdist that does + l('.hg/last-message.txt'), + l('global/one.txt'), + l('global/two.txt'), + l('global/files.x'), + l('global/here.tmp'), + l('f/o/f.oo'), + l('dir/graft-one'), + l('dir/dir2/graft2'), + l('dir3/ok'), + l('dir3/sub/ok.txt'), + ]) + + MANIFEST_IN = DALS("""\ + include ok + include xo + exclude xo + include foo.tmp + include buildout.cfg + global-include *.x + global-include *.txt + global-exclude *.tmp + recursive-include f *.oo + recursive-exclude global *.x + graft dir + prune dir3 + """) + + for line in MANIFEST_IN.split('\n'): + if not line: + continue + file_list.process_template_line(line) + + wanted = [ + 'buildout.cfg', + 'four.txt', + 'ok', + l('.hg/last-message.txt'), + l('dir/graft-one'), + l('dir/dir2/graft2'), + l('f/o/f.oo'), + l('global/one.txt'), + l('global/two.txt'), + ] + file_list.sort() + + assert file_list.files == wanted + + def test_exclude_pattern(self): + # return False if no match + file_list = FileList() + assert not file_list.exclude_pattern('*.py') + + # return True if files match + file_list = FileList() + file_list.files = ['a.py', 'b.py'] + assert file_list.exclude_pattern('*.py') + + # test excludes + file_list = FileList() + file_list.files = ['a.py', 'a.txt'] + file_list.exclude_pattern('*.py') + assert file_list.files == ['a.txt'] + + def test_include_pattern(self): + # return False if no match + file_list = FileList() + file_list.set_allfiles([]) + assert not file_list.include_pattern('*.py') + + # return True if files match + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt']) + assert file_list.include_pattern('*.py') + + # test * matches all files + file_list = FileList() + assert file_list.allfiles is None + file_list.set_allfiles(['a.py', 'b.txt']) + file_list.include_pattern('*') + assert file_list.allfiles == ['a.py', 'b.txt'] + + def test_process_template(self): + l = make_local_path + # invalid lines + file_list = FileList() + for action in ('include', 'exclude', 'global-include', + 'global-exclude', 'recursive-include', + 'recursive-exclude', 'graft', 'prune', 'blarg'): + try: + file_list.process_template_line(action) + except DistutilsTemplateError: + pass + except Exception: + assert False, "Incorrect error thrown" + else: + assert False, "Should have thrown an error" + + # include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) + + file_list.process_template_line('include *.py') + assert file_list.files == ['a.py'] + self.assertNoWarnings() + + file_list.process_template_line('include *.rb') + assert file_list.files == ['a.py'] + self.assertWarnings() + + # exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', l('d/c.py')] + + file_list.process_template_line('exclude *.py') + assert file_list.files == ['b.txt', l('d/c.py')] + self.assertNoWarnings() + + file_list.process_template_line('exclude *.rb') + assert file_list.files == ['b.txt', l('d/c.py')] + self.assertWarnings() + + # global-include + file_list = FileList() + file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) + + file_list.process_template_line('global-include *.py') + assert file_list.files == ['a.py', l('d/c.py')] + self.assertNoWarnings() + + file_list.process_template_line('global-include *.rb') + assert file_list.files == ['a.py', l('d/c.py')] + self.assertWarnings() + + # global-exclude + file_list = FileList() + file_list.files = ['a.py', 'b.txt', l('d/c.py')] + + file_list.process_template_line('global-exclude *.py') + assert file_list.files == ['b.txt'] + self.assertNoWarnings() + + file_list.process_template_line('global-exclude *.rb') + assert file_list.files == ['b.txt'] + self.assertWarnings() + + # recursive-include + file_list = FileList() + file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'), + l('d/d/e.py')]) + + file_list.process_template_line('recursive-include d *.py') + assert file_list.files == [l('d/b.py'), l('d/d/e.py')] + self.assertNoWarnings() + + file_list.process_template_line('recursive-include e *.py') + assert file_list.files == [l('d/b.py'), l('d/d/e.py')] + self.assertWarnings() + + # recursive-exclude + file_list = FileList() + file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')] + + file_list.process_template_line('recursive-exclude d *.py') + assert file_list.files == ['a.py', l('d/c.txt')] + self.assertNoWarnings() + + file_list.process_template_line('recursive-exclude e *.py') + assert file_list.files == ['a.py', l('d/c.txt')] + self.assertWarnings() + + # graft + file_list = FileList() + file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'), + l('f/f.py')]) + + file_list.process_template_line('graft d') + assert file_list.files == [l('d/b.py'), l('d/d/e.py')] + self.assertNoWarnings() + + file_list.process_template_line('graft e') + assert file_list.files == [l('d/b.py'), l('d/d/e.py')] + self.assertWarnings() + + # prune + file_list = FileList() + file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')] + + file_list.process_template_line('prune d') + assert file_list.files == ['a.py', l('f/f.py')] + self.assertNoWarnings() + + file_list.process_template_line('prune e') + assert file_list.files == ['a.py', l('f/f.py')] + self.assertWarnings() -- cgit v1.2.1 From d806e4cf59b252eb2120483347c4f3772f4ca386 Mon Sep 17 00:00:00 2001 From: insiv Date: Wed, 17 Aug 2016 22:07:48 +0700 Subject: Remove whitespace around parameter = sign. --- pkg_resources/__init__.py | 2 +- pkg_resources/tests/test_resources.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2a053b50..8d967c07 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1913,7 +1913,7 @@ class EggMetadata(ZipProvider): self.module_path = importer.archive self._setup_prefix() -_declare_state('dict', _distribution_finders = {}) +_declare_state('dict', _distribution_finders={}) def register_finder(importer_type, distribution_finder): """Register `distribution_finder` to find distributions in sys.path items diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index e0dbb652..e4827fa5 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -114,7 +114,7 @@ class TestDistro: def testDistroMetadata(self): d = Distribution( "/some/path", project_name="FooPkg", py_version="2.4", platform="win32", - metadata = Metadata( + metadata=Metadata( ('PKG-INFO',"Metadata-Version: 1.0\nVersion: 1.3-1\n") ) ) -- cgit v1.2.1 From 37f1dd197243d18667b1d7fee489013d53b3bd06 Mon Sep 17 00:00:00 2001 From: insiv Date: Wed, 17 Aug 2016 22:24:43 +0700 Subject: Add missing whitespace around operators. --- pkg_resources/__init__.py | 56 +++++++++++++++++------------------ pkg_resources/tests/test_resources.py | 12 ++++---- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 2a053b50..a4704c72 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -155,7 +155,7 @@ class _SetuptoolsVersionMixin(object): # pad for numeric comparison yield part.zfill(8) else: - yield '*'+part + yield '*' + part # ensure that alpha/beta/candidate are before final yield '*final' @@ -217,13 +217,13 @@ def __getstate__(): state = {} g = globals() for k, v in _state_vars.items(): - state[k] = g['_sget_'+v](g[k]) + state[k] = g['_sget_' + v](g[k]) return state def __setstate__(state): g = globals() for k, v in state.items(): - g['_sset_'+_state_vars[k]](k, g[k], v) + g['_sset_' + _state_vars[k]](k, g[k], v) return state def _sget_dict(val): @@ -314,7 +314,7 @@ __all__ = [ class ResolutionError(Exception): """Abstract base for dependency resolution errors""" def __repr__(self): - return self.__class__.__name__+repr(self.args) + return self.__class__.__name__ + repr(self.args) class VersionConflict(ResolutionError): @@ -477,7 +477,7 @@ def compatible_platforms(provided, required): XXX Needs compatibility checks for Linux and other unixy OSes. """ - if provided is None or required is None or provided==required: + if provided is None or required is None or provided == required: # easy case return True @@ -732,7 +732,7 @@ class WorkingSet(object): for key in self.entry_keys[item]: if key not in seen: - seen[key]=1 + seen[key] = 1 yield self.by_key[key] def add(self, dist, entry=None, insert=True, replace=False): @@ -1033,7 +1033,7 @@ class Environment(object): is returned. """ return (self.python is None or dist.py_version is None - or dist.py_version==self.python) \ + or dist.py_version == self.python) \ and compatible_platforms(dist.platform, self.platform) def remove(self, dist): @@ -1238,7 +1238,7 @@ class ResourceManager: extract, as it tracks the generated names for possible cleanup later. """ extract_path = self.extraction_path or get_default_cache() - target_path = os.path.join(extract_path, archive_name+'-tmp', *names) + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) try: _bypass_ensure_directory(target_path) except: @@ -1344,7 +1344,7 @@ def get_default_cache(): except KeyError: pass - if os.name!='nt': + if os.name != 'nt': return os.path.expanduser('~/.python-eggs') # XXX this may be locale-specific! @@ -1492,7 +1492,7 @@ class NullProvider: return [] def run_script(self, script_name, namespace): - script = 'scripts/'+script_name + script = 'scripts/' + script_name if not self.has_metadata(script): raise ResolutionError("No script named %r" % script_name) script_text = self.get_metadata(script).replace('\r\n', '\n') @@ -1553,7 +1553,7 @@ class EggProvider(NullProvider): # of multiple eggs; that's why we use module_path instead of .archive path = self.module_path old = None - while path!=old: + while path != old: if _is_unpacked_egg(path): self.egg_name = os.path.basename(path) self.egg_info = os.path.join(path, 'EGG-INFO') @@ -1679,7 +1679,7 @@ class ZipProvider(EggProvider): def __init__(self, module): EggProvider.__init__(self, module) - self.zip_pre = self.loader.archive+os.sep + self.zip_pre = self.loader.archive + os.sep def _zipinfo_name(self, fspath): # Convert a virtual filename (full path to file) into a zipfile subpath @@ -1693,9 +1693,9 @@ class ZipProvider(EggProvider): def _parts(self, zip_path): # Convert a zipfile subpath into an egg-relative path part list. # pseudo-fs path - fspath = self.zip_pre+zip_path - if fspath.startswith(self.egg_root+os.sep): - return fspath[len(self.egg_root)+1:].split(os.sep) + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1:].split(os.sep) raise AssertionError( "%s is not a subpath of %s" % (fspath, self.egg_root) ) @@ -1766,7 +1766,7 @@ class ZipProvider(EggProvider): # so proceed. return real_path # Windows, del old file and retry - elif os.name=='nt': + elif os.name == 'nt': unlink(real_path) rename(tmpnam, real_path) return real_path @@ -1786,7 +1786,7 @@ class ZipProvider(EggProvider): if not os.path.isfile(file_path): return False stat = os.stat(file_path) - if stat.st_size!=size or stat.st_mtime!=timestamp: + if stat.st_size != size or stat.st_mtime != timestamp: return False # check that the contents match zip_contents = self.loader.get_data(zip_path) @@ -1855,10 +1855,10 @@ class FileMetadata(EmptyProvider): self.path = path def has_metadata(self, name): - return name=='PKG-INFO' and os.path.isfile(self.path) + return name == 'PKG-INFO' and os.path.isfile(self.path) def get_metadata(self, name): - if name=='PKG-INFO': + if name == 'PKG-INFO': with io.open(self.path, encoding='utf-8') as f: try: metadata = f.read() @@ -1905,7 +1905,7 @@ class EggMetadata(ZipProvider): def __init__(self, importer): """Create a metadata provider from a zipimporter""" - self.zip_pre = importer.archive+os.sep + self.zip_pre = importer.archive + os.sep self.loader = importer if importer.prefix: self.module_path = os.path.join(importer.archive, importer.prefix) @@ -2117,7 +2117,7 @@ def file_ns_handler(importer, path_item, packageName, module): subpath = os.path.join(path_item, packageName.split('.')[-1]) normalized = _normalize_cached(subpath) for item in module.__path__: - if _normalize_cached(item)==normalized: + if _normalize_cached(item) == normalized: break else: # Only return the path if it's not already there @@ -2294,7 +2294,7 @@ class EntryPoint(object): ep = cls.parse(line, dist) if ep.name in this: raise ValueError("Duplicate entry point", group, ep.name) - this[ep.name]=ep + this[ep.name] = ep return this @classmethod @@ -2356,7 +2356,7 @@ class Distribution(object): @classmethod def from_location(cls, location, basename, metadata=None, **kw): - project_name, version, py_version, platform = [None]*4 + project_name, version, py_version, platform = [None] * 4 basename, ext = os.path.splitext(basename) if ext.lower() in _distributionImpl: cls = _distributionImpl[ext.lower()] @@ -2478,9 +2478,9 @@ class Distribution(object): extra, marker = extra.split(':', 1) if invalid_marker(marker): # XXX warn - reqs=[] + reqs = [] elif not evaluate_marker(marker): - reqs=[] + reqs = [] extra = safe_extra(extra) or None dm.setdefault(extra,[]).extend(parse_requirements(reqs)) return dm @@ -2611,7 +2611,7 @@ class Distribution(object): nloc = _normalize_cached(loc) bdir = os.path.dirname(nloc) - npath= [(p and _normalize_cached(p) or p) for p in path] + npath = [(p and _normalize_cached(p) or p) for p in path] for p, item in enumerate(npath): if item == nloc: @@ -2642,7 +2642,7 @@ class Distribution(object): # p is the spot where we found or inserted loc; now remove duplicates while True: try: - np = npath.index(nloc, p+1) + np = npath.index(nloc, p + 1) except ValueError: break else: @@ -2981,7 +2981,7 @@ def _initialize_master_working_set(): dist.activate(replace=False) del dist add_activation_listener(lambda dist: dist.activate(replace=True), existing=False) - working_set.entries=[] + working_set.entries = [] # match order list(map(working_set.add_entry, sys.path)) globals().update(locals()) diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index e0dbb652..3649a30a 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -164,7 +164,7 @@ class TestDistro: ad.add(Baz) # Activation list now includes resolved dependency - assert list(ws.resolve(parse_requirements("Foo[bar]"), ad)) ==[Foo,Baz] + assert list(ws.resolve(parse_requirements("Foo[bar]"), ad)) == [Foo,Baz] # Requests for conflicting versions produce VersionConflict with pytest.raises(VersionConflict) as vc: ws.resolve(parse_requirements("Foo==1.2\nFoo!=1.2"), ad) @@ -426,7 +426,7 @@ class TestEntryPoints: m = EntryPoint.parse_map({'xyz':self.submap_str}) self.checkSubMap(m['xyz']) assert list(m.keys()) == ['xyz'] - m = EntryPoint.parse_map("[xyz]\n"+self.submap_str) + m = EntryPoint.parse_map("[xyz]\n" + self.submap_str) self.checkSubMap(m['xyz']) assert list(m.keys()) == ['xyz'] with pytest.raises(ValueError): @@ -644,7 +644,7 @@ class TestParsing: def testVersionOrdering(self): def c(s1,s2): p1, p2 = parse_version(s1),parse_version(s2) - assert p1 Date: Wed, 17 Aug 2016 22:47:37 +0700 Subject: Fix spacing after comment hash. --- pkg_resources/tests/test_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index e0dbb652..854bc327 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -153,7 +153,7 @@ class TestDistro: list(map(ws.add, targets)) with pytest.raises(VersionConflict): ws.resolve(parse_requirements("Foo==0.9"), ad) - ws = WorkingSet([]) # reset + ws = WorkingSet([]) # reset # Request an extra that causes an unresolved dependency for "Baz" with pytest.raises(pkg_resources.DistributionNotFound): -- cgit v1.2.1 From bbfaa7817d92bc2e04e4b4fa89d2b9a783f053cd Mon Sep 17 00:00:00 2001 From: insiv Date: Wed, 17 Aug 2016 22:51:31 +0700 Subject: Fix comparison with None. --- pkg_resources/tests/test_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index e0dbb652..963308b2 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -103,7 +103,7 @@ class TestDistro: d = Distribution("/some/path") assert d.py_version == sys.version[:3] - assert d.platform == None + assert d.platform is None def testDistroParse(self): d = dist_from_fn("FooPkg-1.3.post1-py2.4-win32.egg") -- cgit v1.2.1 From 84aec138dc4311c3f23ff78e9916e00b457a1896 Mon Sep 17 00:00:00 2001 From: insiv Date: Wed, 17 Aug 2016 23:05:38 +0700 Subject: Upgrade pyparsing to version 2.1.8 --- pkg_resources/_vendor/pyparsing.py | 3358 +++++++++++++++++++++++++++--------- pkg_resources/_vendor/vendored.txt | 2 +- 2 files changed, 2565 insertions(+), 795 deletions(-) diff --git a/pkg_resources/_vendor/pyparsing.py b/pkg_resources/_vendor/pyparsing.py index 2284cadc..89cffc10 100644 --- a/pkg_resources/_vendor/pyparsing.py +++ b/pkg_resources/_vendor/pyparsing.py @@ -48,7 +48,7 @@ The program outputs the following:: The Python representation of the grammar is quite readable, owing to the self-explanatory class names, and the use of '+', '|' and '^' operators. -The parsed results returned from C{parseString()} can be accessed as a nested list, a dictionary, or an +The parsed results returned from L{I{ParserElement.parseString}} can be accessed as a nested list, a dictionary, or an object with named attributes. The pyparsing module handles some of the problems that are typically vexing when writing text parsers: @@ -57,8 +57,8 @@ The pyparsing module handles some of the problems that are typically vexing when - embedded comments """ -__version__ = "2.0.6" -__versionTime__ = "9 Nov 2015 19:03" +__version__ = "2.1.8" +__versionTime__ = "14 Aug 2016 08:43 UTC" __author__ = "Paul McGuire " import string @@ -70,8 +70,22 @@ import re import sre_constants import collections import pprint -import functools -import itertools +import traceback +import types +from datetime import datetime + +try: + from _thread import RLock +except ImportError: + from threading import RLock + +try: + from collections import OrderedDict as _OrderedDict +except ImportError: + try: + from ordereddict import OrderedDict as _OrderedDict + except ImportError: + _OrderedDict = None #~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) ) @@ -81,21 +95,23 @@ __all__ = [ 'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or', 'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException', 'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException', -'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'Upcase', +'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore', 'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col', 'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString', 'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums', -'htmlComment', 'javaStyleComment', 'keepOriginalText', 'line', 'lineEnd', 'lineStart', 'lineno', +'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno', 'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral', 'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables', 'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', 'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', 'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', +'tokenMap', 'pyparsing_common', ] -PY_3 = sys.version.startswith('3') +system_version = tuple(sys.version_info)[:3] +PY_3 = system_version[0] == 3 if PY_3: _MAX_INT = sys.maxsize basestring = str @@ -123,18 +139,11 @@ else: return str(obj) except UnicodeEncodeError: - # The Python docs (http://docs.python.org/ref/customization.html#l2h-182) - # state that "The return value must be a string object". However, does a - # unicode object (being a subclass of basestring) count as a "string - # object"? - # If so, then return a unicode object: - return unicode(obj) - # Else encode it... but how? There are many choices... :) - # Replace unprintables with escape codes? - #return unicode(obj).encode(sys.getdefaultencoding(), 'backslashreplace_errors') - # Replace unprintables with question marks? - #return unicode(obj).encode(sys.getdefaultencoding(), 'replace') - # ... + # Else encode it + ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace') + xmlcharref = Regex('&#\d+;') + xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:]) + return xmlcharref.transformString(ret) # build list of single arg builtins, tolerant of Python version, that can be used as parse actions singleArgBuiltins = [] @@ -160,7 +169,7 @@ def _xml_escape(data): class _Constants(object): pass -alphas = string.ascii_lowercase + string.ascii_uppercase +alphas = string.ascii_uppercase + string.ascii_lowercase nums = "0123456789" hexnums = nums + "ABCDEFabcdef" alphanums = alphas + nums @@ -180,6 +189,15 @@ class ParseBaseException(Exception): self.msg = msg self.pstr = pstr self.parserElement = elem + self.args = (pstr, loc, msg) + + @classmethod + def _from_exception(cls, pe): + """ + internal factory method to simplify creating one type of ParseException + from another - avoids having __init__ signature conflicts among subclasses + """ + return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) def __getattr__( self, aname ): """supported attributes by name are: @@ -212,15 +230,26 @@ class ParseBaseException(Exception): markerString, line_str[line_column:])) return line_str.strip() def __dir__(self): - return "loc msg pstr parserElement lineno col line " \ - "markInputline __str__ __repr__".split() + return "lineno col line".split() + dir(type(self)) class ParseException(ParseBaseException): - """exception thrown when parse expressions don't match class; - supported attributes by name are: - - lineno - returns the line number of the exception text - - col - returns the column number of the exception text - - line - returns the line containing the exception text + """ + Exception thrown when parse expressions don't match class; + supported attributes by name are: + - lineno - returns the line number of the exception text + - col - returns the column number of the exception text + - line - returns the line containing the exception text + + Example:: + try: + Word(nums).setName("integer").parseString("ABC") + except ParseException as pe: + print(pe) + print("column: {}".format(pe.col)) + + prints:: + Expected integer (at char 0), (line:1, col:1) + column: 1 """ pass @@ -230,12 +259,10 @@ class ParseFatalException(ParseBaseException): pass class ParseSyntaxException(ParseFatalException): - """just like C{L{ParseFatalException}}, but thrown internally when an - C{L{ErrorStop}} ('-' operator) indicates that parsing is to stop immediately because - an unbacktrackable syntax error has been found""" - def __init__(self, pe): - super(ParseSyntaxException, self).__init__( - pe.pstr, pe.loc, pe.msg, pe.parserElement) + """just like L{ParseFatalException}, but thrown internally when an + L{ErrorStop} ('-' operator) indicates that parsing is to stop + immediately because an unbacktrackable syntax error has been found""" + pass #~ class ReparseException(ParseBaseException): #~ """Experimental class - parse actions can raise this exception to cause @@ -251,7 +278,7 @@ class ParseSyntaxException(ParseFatalException): #~ self.reparseLoc = restartLoc class RecursiveGrammarException(Exception): - """exception thrown by C{validate()} if the grammar could be improperly recursive""" + """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive""" def __init__( self, parseElementList ): self.parseElementTrace = parseElementList @@ -269,12 +296,44 @@ class _ParseResultsWithOffset(object): self.tup = (self.tup[0],i) class ParseResults(object): - """Structured parse results, to provide multiple means of access to the parsed data: + """ + Structured parse results, to provide multiple means of access to the parsed data: - as a list (C{len(results)}) - by list index (C{results[0], results[1]}, etc.) - - by attribute (C{results.}) - """ - def __new__(cls, toklist, name=None, asList=True, modal=True ): + - by attribute (C{results.} - see L{ParserElement.setResultsName}) + + Example:: + integer = Word(nums) + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + # equivalent form: + # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + + def test(s, fn=repr): + print("%s -> %s" % (s, fn(eval(s)))) + test("list(result)") + test("result[0]") + test("result['month']") + test("result.day") + test("'month' in result") + test("'minutes' in result") + test("result.dump()", str) + prints:: + list(result) -> ['1999', '/', '12', '/', '31'] + result[0] -> '1999' + result['month'] -> '12' + result.day -> '31' + 'month' in result -> True + 'minutes' in result -> False + result.dump() -> ['1999', '/', '12', '/', '31'] + - day: 31 + - month: 12 + - year: 1999 + """ + def __new__(cls, toklist=None, name=None, asList=True, modal=True ): if isinstance(toklist, cls): return toklist retobj = object.__new__(cls) @@ -283,12 +342,16 @@ class ParseResults(object): # Performance tuning: we construct a *lot* of these, so keep this # constructor as small and fast as possible - def __init__( self, toklist, name=None, asList=True, modal=True, isinstance=isinstance ): + def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ): if self.__doinit: self.__doinit = False self.__name = None self.__parent = None self.__accumNames = {} + self.__asList = asList + self.__modal = modal + if toklist is None: + toklist = [] if isinstance(toklist, list): self.__toklist = toklist[:] elif isinstance(toklist, _generatorType): @@ -331,7 +394,7 @@ class ParseResults(object): if isinstance(v,_ParseResultsWithOffset): self.__tokdict[k] = self.__tokdict.get(k,list()) + [v] sub = v[0] - elif isinstance(k,int): + elif isinstance(k,(int,slice)): self.__toklist[k] = v sub = v else: @@ -354,11 +417,6 @@ class ParseResults(object): removed = list(range(*i.indices(mylen))) removed.reverse() # fixup indices in token dictionary - #~ for name in self.__tokdict: - #~ occurrences = self.__tokdict[name] - #~ for j in removed: - #~ for k, (value, position) in enumerate(occurrences): - #~ occurrences[k] = _ParseResultsWithOffset(value, position - (position > j)) for name,occurrences in self.__tokdict.items(): for j in removed: for k, (value, position) in enumerate(occurrences): @@ -370,39 +428,52 @@ class ParseResults(object): return k in self.__tokdict def __len__( self ): return len( self.__toklist ) - def __bool__(self): return len( self.__toklist ) > 0 + def __bool__(self): return ( not not self.__toklist ) __nonzero__ = __bool__ def __iter__( self ): return iter( self.__toklist ) def __reversed__( self ): return iter( self.__toklist[::-1] ) - def iterkeys( self ): - """Returns all named result keys.""" + def _iterkeys( self ): if hasattr(self.__tokdict, "iterkeys"): return self.__tokdict.iterkeys() else: return iter(self.__tokdict) - def itervalues( self ): - """Returns all named result values.""" - return (self[k] for k in self.iterkeys()) + def _itervalues( self ): + return (self[k] for k in self._iterkeys()) - def iteritems( self ): - return ((k, self[k]) for k in self.iterkeys()) + def _iteritems( self ): + return ((k, self[k]) for k in self._iterkeys()) if PY_3: - keys = iterkeys - values = itervalues - items = iteritems + keys = _iterkeys + """Returns an iterator of all named result keys (Python 3.x only).""" + + values = _itervalues + """Returns an iterator of all named result values (Python 3.x only).""" + + items = _iteritems + """Returns an iterator of all named result key-value tuples (Python 3.x only).""" + else: + iterkeys = _iterkeys + """Returns an iterator of all named result keys (Python 2.x only).""" + + itervalues = _itervalues + """Returns an iterator of all named result values (Python 2.x only).""" + + iteritems = _iteritems + """Returns an iterator of all named result key-value tuples (Python 2.x only).""" + def keys( self ): - """Returns all named result keys.""" + """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x).""" return list(self.iterkeys()) def values( self ): - """Returns all named result values.""" + """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x).""" return list(self.itervalues()) def items( self ): - """Returns all named result keys and values as a list of tuples.""" + """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x).""" return list(self.iteritems()) def haskeys( self ): @@ -411,14 +482,39 @@ class ParseResults(object): return bool(self.__tokdict) def pop( self, *args, **kwargs): - """Removes and returns item at specified index (default=last). - Supports both list and dict semantics for pop(). If passed no - argument or an integer argument, it will use list semantics - and pop tokens from the list of parsed tokens. If passed a - non-integer argument (most likely a string), it will use dict - semantics and pop the corresponding value from any defined - results names. A second default return value argument is - supported, just as in dict.pop().""" + """ + Removes and returns item at specified index (default=C{last}). + Supports both C{list} and C{dict} semantics for C{pop()}. If passed no + argument or an integer argument, it will use C{list} semantics + and pop tokens from the list of parsed tokens. If passed a + non-integer argument (most likely a string), it will use C{dict} + semantics and pop the corresponding value from any defined + results names. A second default return value argument is + supported, just as in C{dict.pop()}. + + Example:: + def remove_first(tokens): + tokens.pop(0) + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321'] + + label = Word(alphas) + patt = label("LABEL") + OneOrMore(Word(nums)) + print(patt.parseString("AAB 123 321").dump()) + + # Use pop() in a parse action to remove named result (note that corresponding value is not + # removed from list form of results) + def remove_LABEL(tokens): + tokens.pop("LABEL") + return tokens + patt.addParseAction(remove_LABEL) + print(patt.parseString("AAB 123 321").dump()) + prints:: + ['AAB', '123', '321'] + - LABEL: AAB + + ['AAB', '123', '321'] + """ if not args: args = [-1] for k,v in kwargs.items(): @@ -438,39 +534,83 @@ class ParseResults(object): return defaultvalue def get(self, key, defaultValue=None): - """Returns named result matching the given key, or if there is no - such name, then returns the given C{defaultValue} or C{None} if no - C{defaultValue} is specified.""" + """ + Returns named result matching the given key, or if there is no + such name, then returns the given C{defaultValue} or C{None} if no + C{defaultValue} is specified. + + Similar to C{dict.get()}. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString("1999/12/31") + print(result.get("year")) # -> '1999' + print(result.get("hour", "not specified")) # -> 'not specified' + print(result.get("hour")) # -> None + """ if key in self: return self[key] else: return defaultValue def insert( self, index, insStr ): - """Inserts new element at location index in the list of parsed tokens.""" + """ + Inserts new element at location index in the list of parsed tokens. + + Similar to C{list.insert()}. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to insert the parse location in the front of the parsed results + def insert_locn(locn, tokens): + tokens.insert(0, locn) + print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321'] + """ self.__toklist.insert(index, insStr) # fixup indices in token dictionary - #~ for name in self.__tokdict: - #~ occurrences = self.__tokdict[name] - #~ for k, (value, position) in enumerate(occurrences): - #~ occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) for name,occurrences in self.__tokdict.items(): for k, (value, position) in enumerate(occurrences): occurrences[k] = _ParseResultsWithOffset(value, position + (position > index)) def append( self, item ): - """Add single element to end of ParseResults list of elements.""" + """ + Add single element to end of ParseResults list of elements. + + Example:: + print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321'] + + # use a parse action to compute the sum of the parsed integers, and add it to the end + def append_sum(tokens): + tokens.append(sum(map(int, tokens))) + print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444] + """ self.__toklist.append(item) def extend( self, itemseq ): - """Add sequence of elements to end of ParseResults list of elements.""" + """ + Add sequence of elements to end of ParseResults list of elements. + + Example:: + patt = OneOrMore(Word(alphas)) + + # use a parse action to append the reverse of the matched strings, to make a palindrome + def make_palindrome(tokens): + tokens.extend(reversed([t[::-1] for t in tokens])) + return ''.join(tokens) + print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' + """ if isinstance(itemseq, ParseResults): self += itemseq else: self.__toklist.extend(itemseq) def clear( self ): - """Clear all elements and results names.""" + """ + Clear all elements and results names. + """ del self.__toklist[:] self.__tokdict.clear() @@ -511,7 +651,11 @@ class ParseResults(object): def __radd__(self, other): if isinstance(other,int) and other == 0: + # useful for merging many ParseResults using sum() builtin return self.copy() + else: + # this may raise a TypeError - so be it + return other + self def __repr__( self ): return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) ) @@ -531,18 +675,60 @@ class ParseResults(object): return out def asList( self ): - """Returns the parse results as a nested list of matching tokens, all converted to strings.""" + """ + Returns the parse results as a nested list of matching tokens, all converted to strings. + + Example:: + patt = OneOrMore(Word(alphas)) + result = patt.parseString("sldkj lsdkj sldkj") + # even though the result prints in string-like form, it is actually a pyparsing ParseResults + print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] + + # Use asList() to create an actual list + result_list = result.asList() + print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] + """ return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist] def asDict( self ): - """Returns the named parse results as dictionary.""" + """ + Returns the named parse results as a nested dictionary. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) + + result_dict = result.asDict() + print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} + + # even though a ParseResults supports dict-like access, sometime you just need to have a dict + import json + print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable + print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"} + """ if PY_3: - return dict( self.items() ) + item_fn = self.items else: - return dict( self.iteritems() ) + item_fn = self.iteritems + + def toItem(obj): + if isinstance(obj, ParseResults): + if obj.haskeys(): + return obj.asDict() + else: + return [toItem(v) for v in obj] + else: + return obj + + return dict((k,toItem(v)) for k,v in item_fn()) def copy( self ): - """Returns a new copy of a C{ParseResults} object.""" + """ + Returns a new copy of a C{ParseResults} object. + """ ret = ParseResults( self.__toklist ) ret.__tokdict = self.__tokdict.copy() ret.__parent = self.__parent @@ -551,7 +737,9 @@ class ParseResults(object): return ret def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ): - """Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.""" + """ + (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names. + """ nl = "\n" out = [] namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items() @@ -617,7 +805,27 @@ class ParseResults(object): return None def getName(self): - """Returns the results name for this token expression.""" + """ + Returns the results name for this token expression. Useful when several + different expressions might match at a particular location. + + Example:: + integer = Word(nums) + ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") + house_number_expr = Suppress('#') + Word(nums, alphanums) + user_data = (Group(house_number_expr)("house_number") + | Group(ssn_expr)("ssn") + | Group(integer)("age")) + user_info = OneOrMore(user_data) + + result = user_info.parseString("22 111-22-3333 #221B") + for item in result: + print(item.getName(), ':', item[0]) + prints:: + age : 22 + ssn : 111-22-3333 + house_number : 221B + """ if self.__name: return self.__name elif self.__parent: @@ -633,40 +841,72 @@ class ParseResults(object): else: return None - def dump(self,indent='',depth=0): - """Diagnostic method for listing out the contents of a C{ParseResults}. - Accepts an optional C{indent} argument so that this string can be embedded - in a nested display of other data.""" + def dump(self, indent='', depth=0, full=True): + """ + Diagnostic method for listing out the contents of a C{ParseResults}. + Accepts an optional C{indent} argument so that this string can be embedded + in a nested display of other data. + + Example:: + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + result = date_str.parseString('12/31/1999') + print(result.dump()) + prints:: + ['12', '/', '31', '/', '1999'] + - day: 1999 + - month: 31 + - year: 12 + """ out = [] NL = '\n' out.append( indent+_ustr(self.asList()) ) - if self.haskeys(): - items = sorted(self.items()) - for k,v in items: - if out: - out.append(NL) - out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) - if isinstance(v,ParseResults): - if v: - out.append( v.dump(indent,depth+1) ) + if full: + if self.haskeys(): + items = sorted(self.items()) + for k,v in items: + if out: + out.append(NL) + out.append( "%s%s- %s: " % (indent,(' '*depth), k) ) + if isinstance(v,ParseResults): + if v: + out.append( v.dump(indent,depth+1) ) + else: + out.append(_ustr(v)) else: out.append(_ustr(v)) - else: - out.append(_ustr(v)) - elif any(isinstance(vv,ParseResults) for vv in self): - v = self - for i,vv in enumerate(v): - if isinstance(vv,ParseResults): - out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) - else: - out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) + elif any(isinstance(vv,ParseResults) for vv in self): + v = self + for i,vv in enumerate(v): + if isinstance(vv,ParseResults): + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),vv.dump(indent,depth+1) )) + else: + out.append("\n%s%s[%d]:\n%s%s%s" % (indent,(' '*(depth)),i,indent,(' '*(depth+1)),_ustr(vv))) return "".join(out) def pprint(self, *args, **kwargs): - """Pretty-printer for parsed results as a list, using the C{pprint} module. - Accepts additional positional or keyword args as defined for the - C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})""" + """ + Pretty-printer for parsed results as a list, using the C{pprint} module. + Accepts additional positional or keyword args as defined for the + C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint}) + + Example:: + ident = Word(alphas, alphanums) + num = Word(nums) + func = Forward() + term = ident | num | Group('(' + func + ')') + func <<= ident + Group(Optional(delimitedList(term))) + result = func.parseString("fna a,b,(fnb c,d,200),100") + result.pprint(width=40) + prints:: + ['fna', + ['a', + 'b', + ['(', 'fnb', ['c', 'd', '200'], ')'], + '100']] + """ pprint.pprint(self.asList(), *args, **kwargs) # add support for pickle protocol @@ -690,8 +930,11 @@ class ParseResults(object): else: self.__parent = None + def __getnewargs__(self): + return self.__toklist, self.__name, self.__asList, self.__modal + def __dir__(self): - return dir(super(ParseResults,self)) + list(self.keys()) + return (dir(type(self)) + list(self.keys())) collections.MutableMapping.register(ParseResults) @@ -771,6 +1014,31 @@ def _trim_arity(func, maxargs=2): return lambda s,l,t: func(t) limit = [0] foundArity = [False] + + # traceback return data structure changed in Py3.5 - normalize back to plain tuples + if system_version[:2] >= (3,5): + def extract_stack(limit=0): + # special handling for Python 3.5.0 - extra deep call stack by 1 + offset = -3 if system_version == (3,5,0) else -2 + frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset] + return [(frame_summary.filename, frame_summary.lineno)] + def extract_tb(tb, limit=0): + frames = traceback.extract_tb(tb, limit=limit) + frame_summary = frames[-1] + return [(frame_summary.filename, frame_summary.lineno)] + else: + extract_stack = traceback.extract_stack + extract_tb = traceback.extract_tb + + # synthesize what would be returned by traceback.extract_stack at the call to + # user's parse action 'func', so that we don't incur call penalty at parse time + + LINE_DIFF = 6 + # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND + # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! + this_line = extract_stack(limit=2)[-1] + pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF) + def wrapper(*args): while 1: try: @@ -778,12 +1046,33 @@ def _trim_arity(func, maxargs=2): foundArity[0] = True return ret except TypeError: - if limit[0] <= maxargs and not foundArity[0]: + # re-raise TypeErrors if they did not come from our arity testing + if foundArity[0]: + raise + else: + try: + tb = sys.exc_info()[-1] + if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth: + raise + finally: + del tb + + if limit[0] <= maxargs: limit[0] += 1 continue raise + + # copy func name to wrapper for sensible debug output + func_name = "" + try: + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + wrapper.__name__ = func_name + return wrapper - + class ParserElement(object): """Abstract base level parser element class.""" DEFAULT_WHITE_CHARS = " \n\t\r" @@ -791,7 +1080,16 @@ class ParserElement(object): @staticmethod def setDefaultWhitespaceChars( chars ): - """Overrides the default whitespace chars + r""" + Overrides the default whitespace chars + + Example:: + # default whitespace chars are space, and newline + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] + + # change to just treat newline as significant + ParserElement.setDefaultWhitespaceChars(" \t") + OneOrMore(Word(alphas)).parseString("abc def\nghi jkl") # -> ['abc', 'def'] """ ParserElement.DEFAULT_WHITE_CHARS = chars @@ -799,8 +1097,22 @@ class ParserElement(object): def inlineLiteralsUsing(cls): """ Set class to be used for inclusion of string literals into a parser. + + Example:: + # default literal class used is Literal + integer = Word(nums) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + + # change to Suppress + ParserElement.inlineLiteralsUsing(Suppress) + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + + date_str.parseString("1999/12/31") # -> ['1999', '12', '31'] """ - ParserElement.literalStringClass = cls + ParserElement._literalStringClass = cls def __init__( self, savelist=False ): self.parseAction = list() @@ -826,8 +1138,21 @@ class ParserElement(object): self.callDuringTry = False def copy( self ): - """Make a copy of this C{ParserElement}. Useful for defining different parse actions - for the same parsing pattern, using copies of the original parse element.""" + """ + Make a copy of this C{ParserElement}. Useful for defining different parse actions + for the same parsing pattern, using copies of the original parse element. + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K") + integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + + print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M")) + prints:: + [5120, 100, 655360, 268435456] + Equivalent form of C{expr.copy()} is just C{expr()}:: + integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M") + """ cpy = copy.copy( self ) cpy.parseAction = self.parseAction[:] cpy.ignoreExprs = self.ignoreExprs[:] @@ -836,7 +1161,13 @@ class ParserElement(object): return cpy def setName( self, name ): - """Define name for this expression, for use in debugging.""" + """ + Define name for this expression, makes debugging and exception messages clearer. + + Example:: + Word(nums).parseString("ABC") # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1) + Word(nums).setName("integer").parseString("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) + """ self.name = name self.errmsg = "Expected " + self.name if hasattr(self,"exception"): @@ -844,15 +1175,24 @@ class ParserElement(object): return self def setResultsName( self, name, listAllMatches=False ): - """Define name for referencing matching tokens as a nested attribute - of the returned parse results. - NOTE: this returns a *copy* of the original C{ParserElement} object; - this is so that the client can define a basic element, such as an - integer, and reference it in multiple places with different names. - - You can also set results names using the abbreviated syntax, - C{expr("name")} in place of C{expr.setResultsName("name")} - - see L{I{__call__}<__call__>}. + """ + Define name for referencing matching tokens as a nested attribute + of the returned parse results. + NOTE: this returns a *copy* of the original C{ParserElement} object; + this is so that the client can define a basic element, such as an + integer, and reference it in multiple places with different names. + + You can also set results names using the abbreviated syntax, + C{expr("name")} in place of C{expr.setResultsName("name")} - + see L{I{__call__}<__call__>}. + + Example:: + date_str = (integer.setResultsName("year") + '/' + + integer.setResultsName("month") + '/' + + integer.setResultsName("day")) + + # equivalent form: + date_str = integer("year") + '/' + integer("month") + '/' + integer("day") """ newself = self.copy() if name.endswith("*"): @@ -881,42 +1221,76 @@ class ParserElement(object): return self def setParseAction( self, *fns, **kwargs ): - """Define action to perform when successfully matching parse element definition. - Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, - C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: - - s = the original string being parsed (see note below) - - loc = the location of the matching substring - - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object - If the functions in fns modify the tokens, they can return them as the return - value from fn, and the modified list of tokens will replace the original. - Otherwise, fn does not need to return any value. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See L{I{parseString}} for more information - on parsing strings containing C{}s, and suggested methods to maintain a - consistent view of the parsed string, the parse location, and line and column - positions within the parsed string. - """ + """ + Define action to perform when successfully matching parse element definition. + Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)}, + C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where: + - s = the original string being parsed (see note below) + - loc = the location of the matching substring + - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object + If the functions in fns modify the tokens, they can return them as the return + value from fn, and the modified list of tokens will replace the original. + Otherwise, fn does not need to return any value. + + Optional keyword arguments: + - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing + + Note: the default parsing behavior is to expand tabs in the input string + before starting the parsing process. See L{I{parseString}} for more information + on parsing strings containing C{}s, and suggested methods to maintain a + consistent view of the parsed string, the parse location, and line and column + positions within the parsed string. + + Example:: + integer = Word(nums) + date_str = integer + '/' + integer + '/' + integer + + date_str.parseString("1999/12/31") # -> ['1999', '/', '12', '/', '31'] + + # use parse action to convert to ints at parse time + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + date_str = integer + '/' + integer + '/' + integer + + # note that integer fields are now ints, not strings + date_str.parseString("1999/12/31") # -> [1999, '/', 12, '/', 31] + """ self.parseAction = list(map(_trim_arity, list(fns))) self.callDuringTry = kwargs.get("callDuringTry", False) return self def addParseAction( self, *fns, **kwargs ): - """Add parse action to expression's list of parse actions. See L{I{setParseAction}}.""" + """ + Add parse action to expression's list of parse actions. See L{I{setParseAction}}. + + See examples in L{I{copy}}. + """ self.parseAction += list(map(_trim_arity, list(fns))) self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) return self def addCondition(self, *fns, **kwargs): """Add a boolean predicate function to expression's list of parse actions. See - L{I{setParseAction}}. Optional keyword argument C{message} can - be used to define a custom message to be used in the raised exception.""" - msg = kwargs.get("message") or "failed user-defined condition" + L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, + functions passed to C{addCondition} need to return boolean success/fail of the condition. + + Optional keyword arguments: + - message = define a custom message to be used in the raised exception + - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException + + Example:: + integer = Word(nums).setParseAction(lambda toks: int(toks[0])) + year_int = integer.copy() + year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") + date_str = year_int + '/' + integer + '/' + integer + + result = date_str.parseString("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1) + """ + msg = kwargs.get("message", "failed user-defined condition") + exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException for fn in fns: def pa(s,l,t): if not bool(_trim_arity(fn)(s,l,t)): - raise ParseException(s,l,msg) - return t + raise exc_type(s,l,msg) self.parseAction.append(pa) self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False) return self @@ -1043,43 +1417,132 @@ class ParserElement(object): return self._parse( instring, loc, doActions=False )[0] except ParseFatalException: raise ParseException( instring, loc, self.errmsg, self) + + def canParseNext(self, instring, loc): + try: + self.tryParse(instring, loc) + except (ParseException, IndexError): + return False + else: + return True + + class _UnboundedCache(object): + def __init__(self): + cache = {} + self.not_in_cache = not_in_cache = object() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + + def clear(self): + cache.clear() + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + + if _OrderedDict is not None: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = _OrderedDict() + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + if len(cache) > size: + cache.popitem(False) + + def clear(self): + cache.clear() + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + + else: + class _FifoCache(object): + def __init__(self, size): + self.not_in_cache = not_in_cache = object() + + cache = {} + key_fifo = collections.deque([], size) + + def get(self, key): + return cache.get(key, not_in_cache) + + def set(self, key, value): + cache[key] = value + if len(cache) > size: + cache.pop(key_fifo.popleft(), None) + key_fifo.append(key) + + def clear(self): + cache.clear() + key_fifo.clear() + + self.get = types.MethodType(get, self) + self.set = types.MethodType(set, self) + self.clear = types.MethodType(clear, self) + + # argument cache for optimizing repeated calls when backtracking through recursive expressions + packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail + packrat_cache_lock = RLock() + packrat_cache_stats = [0, 0] # this method gets repeatedly called during backtracking with the same arguments - # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression def _parseCache( self, instring, loc, doActions=True, callPreParse=True ): - lookup = (self,instring,loc,callPreParse,doActions) - if lookup in ParserElement._exprArgCache: - value = ParserElement._exprArgCache[ lookup ] - if isinstance(value, Exception): - raise value - return (value[0],value[1].copy()) - else: - try: - value = self._parseNoCache( instring, loc, doActions, callPreParse ) - ParserElement._exprArgCache[ lookup ] = (value[0],value[1].copy()) - return value - except ParseBaseException as pe: - pe.__traceback__ = None - ParserElement._exprArgCache[ lookup ] = pe - raise + HIT, MISS = 0, 1 + lookup = (self, instring, loc, callPreParse, doActions) + with ParserElement.packrat_cache_lock: + cache = ParserElement.packrat_cache + value = cache.get(lookup) + if value is cache.not_in_cache: + ParserElement.packrat_cache_stats[MISS] += 1 + try: + value = self._parseNoCache(instring, loc, doActions, callPreParse) + except ParseBaseException as pe: + # cache a copy of the exception, without the traceback + cache.set(lookup, pe.__class__(*pe.args)) + raise + else: + cache.set(lookup, (value[0], value[1].copy())) + return value + else: + ParserElement.packrat_cache_stats[HIT] += 1 + if isinstance(value, Exception): + raise value + return (value[0], value[1].copy()) _parse = _parseNoCache - # argument cache for optimizing repeated calls when backtracking through recursive expressions - _exprArgCache = {} @staticmethod def resetCache(): - ParserElement._exprArgCache.clear() + ParserElement.packrat_cache.clear() + ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats) _packratEnabled = False @staticmethod - def enablePackrat(): + def enablePackrat(cache_size_limit=128): """Enables "packrat" parsing, which adds memoizing to the parsing logic. Repeated parse attempts at the same string location (which happens often in many complex grammars) can immediately return a cached value, instead of re-executing parsing/validating code. Memoizing is done of both valid results and parsing exceptions. - + + Parameters: + - cache_size_limit - (default=C{128}) - if an integer value is provided + will limit the size of the packrat cache; if None is passed, then + the cache size will be unbounded; if 0 is passed, the cache will + be effectively disabled. + This speedup may break existing programs that use parse actions that have side-effects. For this reason, packrat parsing is disabled when you first import pyparsing. To activate the packrat feature, your @@ -1088,32 +1551,45 @@ class ParserElement(object): C{enablePackrat} before calling C{psyco.full()}. If you do not do this, Python will crash. For best results, call C{enablePackrat()} immediately after importing pyparsing. + + Example:: + import pyparsing + pyparsing.ParserElement.enablePackrat() """ if not ParserElement._packratEnabled: ParserElement._packratEnabled = True + if cache_size_limit is None: + ParserElement.packrat_cache = ParserElement._UnboundedCache() + else: + ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit) ParserElement._parse = ParserElement._parseCache def parseString( self, instring, parseAll=False ): - """Execute the parse expression with the given string. - This is the main interface to the client code, once the complete - expression has been built. - - If you want the grammar to require that the entire input string be - successfully parsed, then set C{parseAll} to True (equivalent to ending - the grammar with C{L{StringEnd()}}). - - Note: C{parseString} implicitly calls C{expandtabs()} on the input string, - in order to report proper column numbers in parse actions. - If the input string contains tabs and - the grammar uses parse actions that use the C{loc} argument to index into the - string being parsed, you can ensure you have a consistent view of the input - string by: - - calling C{parseWithTabs} on your grammar before calling C{parseString} - (see L{I{parseWithTabs}}) - - define your parse action using the full C{(s,loc,toks)} signature, and - reference the input string using the parse action's C{s} argument - - explicitly expand the tabs in your input string before calling - C{parseString} + """ + Execute the parse expression with the given string. + This is the main interface to the client code, once the complete + expression has been built. + + If you want the grammar to require that the entire input string be + successfully parsed, then set C{parseAll} to True (equivalent to ending + the grammar with C{L{StringEnd()}}). + + Note: C{parseString} implicitly calls C{expandtabs()} on the input string, + in order to report proper column numbers in parse actions. + If the input string contains tabs and + the grammar uses parse actions that use the C{loc} argument to index into the + string being parsed, you can ensure you have a consistent view of the input + string by: + - calling C{parseWithTabs} on your grammar before calling C{parseString} + (see L{I{parseWithTabs}}) + - define your parse action using the full C{(s,loc,toks)} signature, and + reference the input string using the parse action's C{s} argument + - explictly expand the tabs in your input string before calling + C{parseString} + + Example:: + Word('a').parseString('aaaaabaaa') # -> ['aaaaa'] + Word('a').parseString('aaaaabaaa', parseAll=True) # -> Exception: Expected end of text """ ParserElement.resetCache() if not self.streamlined: @@ -1139,14 +1615,35 @@ class ParserElement(object): return tokens def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ): - """Scan the input string for expression matches. Each match will return the - matching tokens, start location, and end location. May be called with optional - C{maxMatches} argument, to clip scanning after 'n' matches are found. If - C{overlap} is specified, then overlapping matches will be reported. - - Note that the start and end locations are reported relative to the string - being parsed. See L{I{parseString}} for more information on parsing - strings with embedded tabs.""" + """ + Scan the input string for expression matches. Each match will return the + matching tokens, start location, and end location. May be called with optional + C{maxMatches} argument, to clip scanning after 'n' matches are found. If + C{overlap} is specified, then overlapping matches will be reported. + + Note that the start and end locations are reported relative to the string + being parsed. See L{I{parseString}} for more information on parsing + strings with embedded tabs. + + Example:: + source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" + print(source) + for tokens,start,end in Word(alphas).scanString(source): + print(' '*start + '^'*(end-start)) + print(' '*start + tokens[0]) + + prints:: + + sldjf123lsdjjkf345sldkjf879lkjsfd987 + ^^^^^ + sldjf + ^^^^^^^ + lsdjjkf + ^^^^^^ + sldkjf + ^^^^^^ + lkjsfd + """ if not self.streamlined: self.streamline() for e in self.ignoreExprs: @@ -1189,12 +1686,22 @@ class ParserElement(object): raise exc def transformString( self, instring ): - """Extension to C{L{scanString}}, to modify matching text with modified tokens that may - be returned from a parse action. To use C{transformString}, define a grammar and - attach a parse action to it that modifies the returned token list. - Invoking C{transformString()} on a target string will then scan for matches, - and replace the matched text patterns according to the logic in the parse - action. C{transformString()} returns the resulting transformed string.""" + """ + Extension to C{L{scanString}}, to modify matching text with modified tokens that may + be returned from a parse action. To use C{transformString}, define a grammar and + attach a parse action to it that modifies the returned token list. + Invoking C{transformString()} on a target string will then scan for matches, + and replace the matched text patterns according to the logic in the parse + action. C{transformString()} returns the resulting transformed string. + + Example:: + wd = Word(alphas) + wd.setParseAction(lambda toks: toks[0].title()) + + print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york.")) + Prints:: + Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. + """ out = [] lastE = 0 # force preservation of s, to minimize unwanted transformation of string, and to @@ -1222,9 +1729,18 @@ class ParserElement(object): raise exc def searchString( self, instring, maxMatches=_MAX_INT ): - """Another extension to C{L{scanString}}, simplifying the access to the tokens found - to match the given parse expression. May be called with optional - C{maxMatches} argument, to clip searching after 'n' matches are found. + """ + Another extension to C{L{scanString}}, simplifying the access to the tokens found + to match the given parse expression. May be called with optional + C{maxMatches} argument, to clip searching after 'n' matches are found. + + Example:: + # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters + cap_word = Word(alphas.upper(), alphas.lower()) + + print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")) + prints:: + ['More', 'Iron', 'Lead', 'Gold', 'I'] """ try: return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ]) @@ -1235,10 +1751,34 @@ class ParserElement(object): # catch and re-raise exception from here, clears out pyparsing internal stack trace raise exc + def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False): + """ + Generator method to split a string using the given expression as a separator. + May be called with optional C{maxsplit} argument, to limit the number of splits; + and the optional C{includeSeparators} argument (default=C{False}), if the separating + matching text should be included in the split results. + + Example:: + punc = oneOf(list(".,;:/-!?")) + print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) + prints:: + ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] + """ + splits = 0 + last = 0 + for t,s,e in self.scanString(instring, maxMatches=maxsplit): + yield instring[last:s] + if includeSeparators: + yield t[0] + last = e + yield instring[last:] + def __add__(self, other ): - """Implementation of + operator - returns C{L{And}}""" + """ + Implementation of + operator - returns C{L{And}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1246,9 +1786,11 @@ class ParserElement(object): return And( [ self, other ] ) def __radd__(self, other ): - """Implementation of + operator when left operand is not a C{L{ParserElement}}""" + """ + Implementation of + operator when left operand is not a C{L{ParserElement}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1256,9 +1798,11 @@ class ParserElement(object): return other + self def __sub__(self, other): - """Implementation of - operator, returns C{L{And}} with error stop""" + """ + Implementation of - operator, returns C{L{And}} with error stop + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1266,9 +1810,11 @@ class ParserElement(object): return And( [ self, And._ErrorStop(), other ] ) def __rsub__(self, other ): - """Implementation of - operator when left operand is not a C{L{ParserElement}}""" + """ + Implementation of - operator when left operand is not a C{L{ParserElement}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1276,24 +1822,24 @@ class ParserElement(object): return other - self def __mul__(self,other): - """Implementation of * operator, allows use of C{expr * 3} in place of - C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer - tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples - may also include C{None} as in: - - C{expr*(n,None)} or C{expr*(n,)} is equivalent + """ + Implementation of * operator, allows use of C{expr * 3} in place of + C{expr + expr + expr}. Expressions may also me multiplied by a 2-integer + tuple, similar to C{{min,max}} multipliers in regular expressions. Tuples + may also include C{None} as in: + - C{expr*(n,None)} or C{expr*(n,)} is equivalent to C{expr*n + L{ZeroOrMore}(expr)} (read as "at least n instances of C{expr}") - - C{expr*(None,n)} is equivalent to C{expr*(0,n)} + - C{expr*(None,n)} is equivalent to C{expr*(0,n)} (read as "0 to n instances of C{expr}") - - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} - - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} - - Note that C{expr*(None,n)} does not raise an exception if - more than n exprs exist in the input stream; that is, - C{expr*(None,n)} does not enforce a maximum number of expr - occurrences. If this behavior is desired, then write - C{expr*(None,n) + ~expr} - + - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)} + - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)} + + Note that C{expr*(None,n)} does not raise an exception if + more than n exprs exist in the input stream; that is, + C{expr*(None,n)} does not enforce a maximum number of expr + occurrences. If this behavior is desired, then write + C{expr*(None,n) + ~expr} """ if isinstance(other,int): minElements, optElements = other,0 @@ -1347,9 +1893,11 @@ class ParserElement(object): return self.__mul__(other) def __or__(self, other ): - """Implementation of | operator - returns C{L{MatchFirst}}""" + """ + Implementation of | operator - returns C{L{MatchFirst}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1357,9 +1905,11 @@ class ParserElement(object): return MatchFirst( [ self, other ] ) def __ror__(self, other ): - """Implementation of | operator when left operand is not a C{L{ParserElement}}""" + """ + Implementation of | operator when left operand is not a C{L{ParserElement}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1367,9 +1917,11 @@ class ParserElement(object): return other | self def __xor__(self, other ): - """Implementation of ^ operator - returns C{L{Or}}""" + """ + Implementation of ^ operator - returns C{L{Or}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1377,9 +1929,11 @@ class ParserElement(object): return Or( [ self, other ] ) def __rxor__(self, other ): - """Implementation of ^ operator when left operand is not a C{L{ParserElement}}""" + """ + Implementation of ^ operator when left operand is not a C{L{ParserElement}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1387,9 +1941,11 @@ class ParserElement(object): return other ^ self def __and__(self, other ): - """Implementation of & operator - returns C{L{Each}}""" + """ + Implementation of & operator - returns C{L{Each}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1397,9 +1953,11 @@ class ParserElement(object): return Each( [ self, other ] ) def __rand__(self, other ): - """Implementation of & operator when left operand is not a C{L{ParserElement}}""" + """ + Implementation of & operator when left operand is not a C{L{ParserElement}} + """ if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) if not isinstance( other, ParserElement ): warnings.warn("Cannot combine element of type %s with ParserElement" % type(other), SyntaxWarning, stacklevel=2) @@ -1407,41 +1965,49 @@ class ParserElement(object): return other & self def __invert__( self ): - """Implementation of ~ operator - returns C{L{NotAny}}""" + """ + Implementation of ~ operator - returns C{L{NotAny}} + """ return NotAny( self ) def __call__(self, name=None): - """Shortcut for C{L{setResultsName}}, with C{listAllMatches=default}:: - userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") - could be written as:: - userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") - - If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be - passed as C{True}. + """ + Shortcut for C{L{setResultsName}}, with C{listAllMatches=default}. + + If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be + passed as C{True}. - If C{name} is omitted, same as calling C{L{copy}}. - """ + If C{name} is omitted, same as calling C{L{copy}}. + + Example:: + # these are equivalent + userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno") + userdata = Word(alphas)("name") + Word(nums+"-")("socsecno") + """ if name is not None: return self.setResultsName(name) else: return self.copy() def suppress( self ): - """Suppresses the output of this C{ParserElement}; useful to keep punctuation from - cluttering up returned output. + """ + Suppresses the output of this C{ParserElement}; useful to keep punctuation from + cluttering up returned output. """ return Suppress( self ) def leaveWhitespace( self ): - """Disables the skipping of whitespace before matching the characters in the - C{ParserElement}'s defined pattern. This is normally only used internally by - the pyparsing module, but may be needed in some whitespace-sensitive grammars. + """ + Disables the skipping of whitespace before matching the characters in the + C{ParserElement}'s defined pattern. This is normally only used internally by + the pyparsing module, but may be needed in some whitespace-sensitive grammars. """ self.skipWhitespace = False return self def setWhitespaceChars( self, chars ): - """Overrides the default whitespace chars + """ + Overrides the default whitespace chars """ self.skipWhitespace = True self.whiteChars = chars @@ -1449,26 +2015,41 @@ class ParserElement(object): return self def parseWithTabs( self ): - """Overrides default behavior to expand C{}s to spaces before parsing the input string. - Must be called before C{parseString} when the input grammar contains elements that - match C{} characters.""" + """ + Overrides default behavior to expand C{}s to spaces before parsing the input string. + Must be called before C{parseString} when the input grammar contains elements that + match C{} characters. + """ self.keepTabs = True return self def ignore( self, other ): - """Define expression to be ignored (e.g., comments) while doing pattern - matching; may be called repeatedly, to define multiple comment or other - ignorable patterns. """ + Define expression to be ignored (e.g., comments) while doing pattern + matching; may be called repeatedly, to define multiple comment or other + ignorable patterns. + + Example:: + patt = OneOrMore(Word(alphas)) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj'] + + patt.ignore(cStyleComment) + patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd'] + """ + if isinstance(other, basestring): + other = Suppress(other) + if isinstance( other, Suppress ): if other not in self.ignoreExprs: - self.ignoreExprs.append( other.copy() ) + self.ignoreExprs.append(other) else: self.ignoreExprs.append( Suppress( other.copy() ) ) return self def setDebugActions( self, startAction, successAction, exceptionAction ): - """Enable display of debugging messages while doing pattern matching.""" + """ + Enable display of debugging messages while doing pattern matching. + """ self.debugActions = (startAction or _defaultStartDebugAction, successAction or _defaultSuccessDebugAction, exceptionAction or _defaultExceptionDebugAction) @@ -1476,8 +2057,39 @@ class ParserElement(object): return self def setDebug( self, flag=True ): - """Enable display of debugging messages while doing pattern matching. - Set C{flag} to True to enable, False to disable.""" + """ + Enable display of debugging messages while doing pattern matching. + Set C{flag} to True to enable, False to disable. + + Example:: + wd = Word(alphas).setName("alphaword") + integer = Word(nums).setName("numword") + term = wd | integer + + # turn on debugging for wd + wd.setDebug() + + OneOrMore(term).parseString("abc 123 xyz 890") + + prints:: + Match alphaword at loc 0(1,1) + Matched alphaword -> ['abc'] + Match alphaword at loc 3(1,4) + Exception raised:Expected alphaword (at char 4), (line:1, col:5) + Match alphaword at loc 7(1,8) + Matched alphaword -> ['xyz'] + Match alphaword at loc 11(1,12) + Exception raised:Expected alphaword (at char 12), (line:1, col:13) + Match alphaword at loc 15(1,16) + Exception raised:Expected alphaword (at char 15), (line:1, col:16) + + The output shown is that produced by the default debug actions. Prior to attempting + to match the C{wd} expression, the debugging message C{"Match at loc (,)"} + is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} + message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, + which makes debugging and exception messages easier to understand - for instance, the default + name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}. + """ if flag: self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction ) else: @@ -1499,20 +2111,22 @@ class ParserElement(object): pass def validate( self, validateTrace=[] ): - """Check defined expressions for valid structure, check for infinite recursive definitions.""" + """ + Check defined expressions for valid structure, check for infinite recursive definitions. + """ self.checkRecursion( [] ) def parseFile( self, file_or_filename, parseAll=False ): - """Execute the parse expression on the given file or filename. - If a filename is specified (instead of a file object), - the entire file is opened, read, and closed before parsing. + """ + Execute the parse expression on the given file or filename. + If a filename is specified (instead of a file object), + the entire file is opened, read, and closed before parsing. """ try: file_contents = file_or_filename.read() except AttributeError: - f = open(file_or_filename, "r") - file_contents = f.read() - f.close() + with open(file_or_filename, "r") as f: + file_contents = f.read() try: return self.parseString(file_contents, parseAll) except ParseBaseException as exc: @@ -1524,13 +2138,9 @@ class ParserElement(object): def __eq__(self,other): if isinstance(other, ParserElement): - return self is other or self.__dict__ == other.__dict__ + return self is other or vars(self) == vars(other) elif isinstance(other, basestring): - try: - self.parseString(_ustr(other), parseAll=True) - return True - except ParseBaseException: - return False + return self.matches(other) else: return super(ParserElement,self)==other @@ -1546,40 +2156,161 @@ class ParserElement(object): def __rne__(self,other): return not (self == other) - def runTests(self, tests, parseAll=False): - """Execute the parse expression on a series of test strings, showing each - test, the parsed results or where the parse failed. Quick and easy way to - run a parse expression against a list of sample strings. + def matches(self, testString, parseAll=True): + """ + Method for quick testing of a parser against a test string. Good for simple + inline microtests of sub expressions while building up larger parser.0 - Parameters: - - tests - a list of separate test strings, or a multiline string of test strings - - parseAll - (default=False) - flag to pass to C{L{parseString}} when running tests + Parameters: + - testString - to test against this expression for a match + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + + Example:: + expr = Word(nums) + assert expr.matches("100") + """ + try: + self.parseString(_ustr(testString), parseAll=parseAll) + return True + except ParseBaseException: + return False + + def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False): + """ + Execute the parse expression on a series of test strings, showing each + test, the parsed results or where the parse failed. Quick and easy way to + run a parse expression against a list of sample strings. + + Parameters: + - tests - a list of separate test strings, or a multiline string of test strings + - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests + - comment - (default=C{'#'}) - expression for indicating embedded comments in the test + string; pass None to disable comment filtering + - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline; + if False, only dump nested list + - printResults - (default=C{True}) prints test output to stdout + - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing + + Returns: a (success, results) tuple, where success indicates that all tests succeeded + (or failed if C{failureTests} is True), and the results contain a list of lines of each + test's output + + Example:: + number_expr = pyparsing_common.number.copy() + + result = number_expr.runTests(''' + # unsigned integer + 100 + # negative integer + -100 + # float with scientific notation + 6.02e23 + # integer with scientific notation + 1e-12 + ''') + print("Success" if result[0] else "Failed!") + + result = number_expr.runTests(''' + # stray character + 100Z + # missing leading digit before '.' + -.100 + # too many '.' + 3.14.159 + ''', failureTests=True) + print("Success" if result[0] else "Failed!") + prints:: + # unsigned integer + 100 + [100] + + # negative integer + -100 + [-100] + + # float with scientific notation + 6.02e23 + [6.02e+23] + + # integer with scientific notation + 1e-12 + [1e-12] + + Success + + # stray character + 100Z + ^ + FAIL: Expected end of text (at char 3), (line:1, col:4) + + # missing leading digit before '.' + -.100 + ^ + FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) + + # too many '.' + 3.14.159 + ^ + FAIL: Expected end of text (at char 4), (line:1, col:5) + + Success """ if isinstance(tests, basestring): - tests = map(str.strip, tests.splitlines()) + tests = list(map(str.strip, tests.rstrip().splitlines())) + if isinstance(comment, basestring): + comment = Literal(comment) + allResults = [] + comments = [] + success = True for t in tests: - out = [t] + if comment is not None and comment.matches(t, False) or comments and not t: + comments.append(t) + continue + if not t: + continue + out = ['\n'.join(comments), t] + comments = [] try: - out.append(self.parseString(t, parseAll=parseAll).dump()) - except ParseException as pe: + result = self.parseString(t, parseAll=parseAll) + out.append(result.dump(full=fullDump)) + success = success and not failureTests + except ParseBaseException as pe: + fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" if '\n' in t: out.append(line(pe.loc, t)) - out.append(' '*(col(pe.loc,t)-1) + '^') + out.append(' '*(col(pe.loc,t)-1) + '^' + fatal) else: - out.append(' '*pe.loc + '^') - out.append(str(pe)) - out.append('') - print('\n'.join(out)) + out.append(' '*pe.loc + '^' + fatal) + out.append("FAIL: " + str(pe)) + success = success and failureTests + result = pe + except Exception as exc: + out.append("FAIL-EXCEPTION: " + str(exc)) + success = success and failureTests + result = exc + + if printResults: + if fullDump: + out.append('') + print('\n'.join(out)) + + allResults.append((t, result)) + + return success, allResults class Token(ParserElement): - """Abstract C{ParserElement} subclass, for defining atomic matching patterns.""" + """ + Abstract C{ParserElement} subclass, for defining atomic matching patterns. + """ def __init__( self ): super(Token,self).__init__( savelist=False ) class Empty(Token): - """An empty token, will always match.""" + """ + An empty token, will always match. + """ def __init__( self ): super(Empty,self).__init__() self.name = "Empty" @@ -1588,7 +2319,9 @@ class Empty(Token): class NoMatch(Token): - """A token that will never match.""" + """ + A token that will never match. + """ def __init__( self ): super(NoMatch,self).__init__() self.name = "NoMatch" @@ -1601,7 +2334,19 @@ class NoMatch(Token): class Literal(Token): - """Token to exactly match a specified string.""" + """ + Token to exactly match a specified string. + + Example:: + Literal('blah').parseString('blah') # -> ['blah'] + Literal('blah').parseString('blahfooblah') # -> ['blah'] + Literal('blah').parseString('bla') # -> Exception: Expected "blah" + + For case-insensitive matching, use L{CaselessLiteral}. + + For keyword matching (force word break before and after the matched string), + use L{Keyword} or L{CaselessKeyword}. + """ def __init__( self, matchString ): super(Literal,self).__init__() self.match = matchString @@ -1627,17 +2372,24 @@ class Literal(Token): return loc+self.matchLen, self.match raise ParseException(instring, loc, self.errmsg, self) _L = Literal -ParserElement.literalStringClass = Literal +ParserElement._literalStringClass = Literal class Keyword(Token): - """Token to exactly match a specified string as a keyword, that is, it must be - immediately followed by a non-keyword character. Compare with C{L{Literal}}:: - Literal("if") will match the leading C{'if'} in C{'ifAndOnlyIf'}. - Keyword("if") will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} - Accepts two optional constructor arguments in addition to the keyword string: - C{identChars} is a string of characters that would be valid identifier characters, - defaulting to all alphanumerics + "_" and "$"; C{caseless} allows case-insensitive - matching, default is C{False}. + """ + Token to exactly match a specified string as a keyword, that is, it must be + immediately followed by a non-keyword character. Compare with C{L{Literal}}: + - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}. + - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'} + Accepts two optional constructor arguments in addition to the keyword string: + - C{identChars} is a string of characters that would be valid identifier characters, + defaulting to all alphanumerics + "_" and "$" + - C{caseless} allows case-insensitive matching, default is C{False}. + + Example:: + Keyword("start").parseString("start") # -> ['start'] + Keyword("start").parseString("starting") # -> Exception + + For case-insensitive matching, use L{CaselessKeyword}. """ DEFAULT_KEYWORD_CHARS = alphanums+"_$" @@ -1686,9 +2438,15 @@ class Keyword(Token): Keyword.DEFAULT_KEYWORD_CHARS = chars class CaselessLiteral(Literal): - """Token to match a specified string, ignoring case of letters. - Note: the matched results will always be in the case of the given - match string, NOT the case of the input text. + """ + Token to match a specified string, ignoring case of letters. + Note: the matched results will always be in the case of the given + match string, NOT the case of the input text. + + Example:: + OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD'] + + (Contrast with example for L{CaselessKeyword}.) """ def __init__( self, matchString ): super(CaselessLiteral,self).__init__( matchString.upper() ) @@ -1703,6 +2461,14 @@ class CaselessLiteral(Literal): raise ParseException(instring, loc, self.errmsg, self) class CaselessKeyword(Keyword): + """ + Caseless version of L{Keyword}. + + Example:: + OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD'] + + (Contrast with example for L{CaselessLiteral}.) + """ def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) @@ -1713,16 +2479,51 @@ class CaselessKeyword(Keyword): raise ParseException(instring, loc, self.errmsg, self) class Word(Token): - """Token for matching words composed of allowed character sets. - Defined with string containing all allowed initial characters, - an optional string containing allowed body characters (if omitted, - defaults to the initial character set), and an optional minimum, - maximum, and/or exact length. The default value for C{min} is 1 (a - minimum value < 1 is not valid); the default values for C{max} and C{exact} - are 0, meaning no maximum or exact length restriction. An optional - C{exclude} parameter can list characters that might be found in - the input C{bodyChars} string; useful to define a word of all printables - except for one or two characters, for instance. + """ + Token for matching words composed of allowed character sets. + Defined with string containing all allowed initial characters, + an optional string containing allowed body characters (if omitted, + defaults to the initial character set), and an optional minimum, + maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. An optional + C{excludeChars} parameter can list characters that might be found in + the input C{bodyChars} string; useful to define a word of all printables + except for one or two characters, for instance. + + L{srange} is useful for defining custom character set strings for defining + C{Word} expressions, using range notation from regular expression character sets. + + A common mistake is to use C{Word} to match a specific literal string, as in + C{Word("Address")}. Remember that C{Word} uses the string argument to define + I{sets} of matchable characters. This expression would match "Add", "AAA", + "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'. + To match an exact literal string, use L{Literal} or L{Keyword}. + + pyparsing includes helper strings for building Words: + - L{alphas} + - L{nums} + - L{alphanums} + - L{hexnums} + - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.) + - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.) + - L{printables} (any non-whitespace character) + + Example:: + # a word composed of digits + integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) + + # a word with a leading capital, and zero or more lowercase + capital_word = Word(alphas.upper(), alphas.lower()) + + # hostnames are alphanumeric, with leading alpha, and '-' + hostname = Word(alphas, alphanums+'-') + + # roman numeral (not a strict parser, accepts invalid mix of characters) + roman = Word("IVXLCDM") + + # any string of non-whitespace characters, except for ',' + csv_value = Word(printables, excludeChars=",") """ def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ): super(Word,self).__init__() @@ -1837,8 +2638,17 @@ class Word(Token): class Regex(Token): - """Token for matching strings that match a given regular expression. - Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + """ + Token for matching strings that match a given regular expression. + Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module. + If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as + named parse results. + + Example:: + realnum = Regex(r"[+-]?\d+\.\d*") + date = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)') + # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression + roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") """ compiledREtype = type(re.compile("[A-Z]")) def __init__( self, pattern, flags=0): @@ -1846,7 +2656,7 @@ class Regex(Token): super(Regex,self).__init__() if isinstance(pattern, basestring): - if len(pattern) == 0: + if not pattern: warnings.warn("null string passed to Regex; use Empty() instead", SyntaxWarning, stacklevel=2) @@ -1901,23 +2711,36 @@ class Regex(Token): class QuotedString(Token): - """Token for matching strings that are delimited by quoting characters. + r""" + Token for matching strings that are delimited by quoting characters. + + Defined with the following parameters: + - quoteChar - string of one or more characters defining the quote delimiting string + - escChar - character to escape quotes, typically backslash (default=C{None}) + - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None}) + - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) + - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) + - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) + - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True}) + + Example:: + qs = QuotedString('"') + print(qs.searchString('lsjdf "This is the quote" sldjf')) + complex_qs = QuotedString('{{', endQuoteChar='}}') + print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf')) + sql_qs = QuotedString('"', escQuote='""') + print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) + prints:: + [['This is the quote']] + [['This is the "quote"']] + [['This is the quote with "embedded" quotes']] """ - def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None): - """ - Defined with the following parameters: - - quoteChar - string of one or more characters defining the quote delimiting string - - escChar - character to escape quotes, typically backslash (default=None) - - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=None) - - multiline - boolean indicating whether quotes can span multiple lines (default=C{False}) - - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True}) - - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar) - """ + def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True): super(QuotedString,self).__init__() # remove white space from quote chars - wont work anyway quoteChar = quoteChar.strip() - if len(quoteChar) == 0: + if not quoteChar: warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) raise SyntaxError() @@ -1925,7 +2748,7 @@ class QuotedString(Token): endQuoteChar = quoteChar else: endQuoteChar = endQuoteChar.strip() - if len(endQuoteChar) == 0: + if not endQuoteChar: warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2) raise SyntaxError() @@ -1937,6 +2760,7 @@ class QuotedString(Token): self.escChar = escChar self.escQuote = escQuote self.unquoteResults = unquoteResults + self.convertWhitespaceEscapes = convertWhitespaceEscapes if multiline: self.flags = re.MULTILINE | re.DOTALL @@ -1990,6 +2814,17 @@ class QuotedString(Token): ret = ret[self.quoteCharLen:-self.endQuoteCharLen] if isinstance(ret,basestring): + # replace escaped whitespace + if '\\' in ret and self.convertWhitespaceEscapes: + ws_map = { + r'\t' : '\t', + r'\n' : '\n', + r'\f' : '\f', + r'\r' : '\r', + } + for wslit,wschar in ws_map.items(): + ret = ret.replace(wslit, wschar) + # replace escaped characters if self.escChar: ret = re.sub(self.escCharReplacePattern,"\g<1>",ret) @@ -2013,11 +2848,20 @@ class QuotedString(Token): class CharsNotIn(Token): - """Token for matching words composed of characters *not* in a given set. - Defined with string containing all disallowed characters, and an optional - minimum, maximum, and/or exact length. The default value for C{min} is 1 (a - minimum value < 1 is not valid); the default values for C{max} and C{exact} - are 0, meaning no maximum or exact length restriction. + """ + Token for matching words composed of characters I{not} in a given set (will + include whitespace in matched characters if not listed in the provided exclusion set - see example). + Defined with string containing all disallowed characters, and an optional + minimum, maximum, and/or exact length. The default value for C{min} is 1 (a + minimum value < 1 is not valid); the default values for C{max} and C{exact} + are 0, meaning no maximum or exact length restriction. + + Example:: + # define a comma-separated-value as anything that is not a ',' + csv_value = CharsNotIn(',') + print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213")) + prints:: + ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] """ def __init__( self, notChars, min=1, max=0, exact=0 ): super(CharsNotIn,self).__init__() @@ -2075,11 +2919,13 @@ class CharsNotIn(Token): return self.strRepr class White(Token): - """Special matching class for matching whitespace. Normally, whitespace is ignored - by pyparsing grammars. This class is included when some whitespace structures - are significant. Define with a string containing the whitespace characters to be - matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, - as defined for the C{L{Word}} class.""" + """ + Special matching class for matching whitespace. Normally, whitespace is ignored + by pyparsing grammars. This class is included when some whitespace structures + are significant. Define with a string containing the whitespace characters to be + matched; default is C{" \\t\\r\\n"}. Also takes optional C{min}, C{max}, and C{exact} arguments, + as defined for the C{L{Word}} class. + """ whiteStrs = { " " : "", "\t": "", @@ -2131,7 +2977,9 @@ class _PositionToken(Token): self.mayIndexError = False class GoToColumn(_PositionToken): - """Token to advance to a specific column of input text; useful for tabular report scraping.""" + """ + Token to advance to a specific column of input text; useful for tabular report scraping. + """ def __init__( self, colno ): super(GoToColumn,self).__init__() self.col = colno @@ -2154,7 +3002,9 @@ class GoToColumn(_PositionToken): return newloc, ret class LineStart(_PositionToken): - """Matches if current position is at the beginning of a line within the parse string""" + """ + Matches if current position is at the beginning of a line within the parse string + """ def __init__( self ): super(LineStart,self).__init__() self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) @@ -2174,7 +3024,9 @@ class LineStart(_PositionToken): return loc, [] class LineEnd(_PositionToken): - """Matches if current position is at the end of a line within the parse string""" + """ + Matches if current position is at the end of a line within the parse string + """ def __init__( self ): super(LineEnd,self).__init__() self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") ) @@ -2192,7 +3044,9 @@ class LineEnd(_PositionToken): raise ParseException(instring, loc, self.errmsg, self) class StringStart(_PositionToken): - """Matches if current position is at the beginning of the parse string""" + """ + Matches if current position is at the beginning of the parse string + """ def __init__( self ): super(StringStart,self).__init__() self.errmsg = "Expected start of text" @@ -2205,7 +3059,9 @@ class StringStart(_PositionToken): return loc, [] class StringEnd(_PositionToken): - """Matches if current position is at the end of the parse string""" + """ + Matches if current position is at the end of the parse string + """ def __init__( self ): super(StringEnd,self).__init__() self.errmsg = "Expected end of text" @@ -2221,11 +3077,12 @@ class StringEnd(_PositionToken): raise ParseException(instring, loc, self.errmsg, self) class WordStart(_PositionToken): - """Matches if the current position is at the beginning of a Word, and - is not preceded by any character in a given set of C{wordChars} - (default=C{printables}). To emulate the C{\b} behavior of regular expressions, - use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of - the string being parsed, or at the beginning of a line. + """ + Matches if the current position is at the beginning of a Word, and + is not preceded by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of + the string being parsed, or at the beginning of a line. """ def __init__(self, wordChars = printables): super(WordStart,self).__init__() @@ -2240,11 +3097,12 @@ class WordStart(_PositionToken): return loc, [] class WordEnd(_PositionToken): - """Matches if the current position is at the end of a Word, and - is not followed by any character in a given set of C{wordChars} - (default=C{printables}). To emulate the C{\b} behavior of regular expressions, - use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of - the string being parsed, or at the end of a line. + """ + Matches if the current position is at the end of a Word, and + is not followed by any character in a given set of C{wordChars} + (default=C{printables}). To emulate the C{\b} behavior of regular expressions, + use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of + the string being parsed, or at the end of a line. """ def __init__(self, wordChars = printables): super(WordEnd,self).__init__() @@ -2262,18 +3120,21 @@ class WordEnd(_PositionToken): class ParseExpression(ParserElement): - """Abstract subclass of ParserElement, for combining and post-processing parsed tokens.""" + """ + Abstract subclass of ParserElement, for combining and post-processing parsed tokens. + """ def __init__( self, exprs, savelist = False ): super(ParseExpression,self).__init__(savelist) if isinstance( exprs, _generatorType ): exprs = list(exprs) if isinstance( exprs, basestring ): - self.exprs = [ Literal( exprs ) ] - elif isinstance( exprs, collections.Sequence ): + self.exprs = [ ParserElement._literalStringClass( exprs ) ] + elif isinstance( exprs, collections.Iterable ): + exprs = list(exprs) # if sequence of strings provided, wrap with Literal if all(isinstance(expr, basestring) for expr in exprs): - exprs = map(Literal, exprs) + exprs = map(ParserElement._literalStringClass, exprs) self.exprs = list(exprs) else: try: @@ -2351,7 +3212,7 @@ class ParseExpression(ParserElement): self.mayReturnEmpty |= other.mayReturnEmpty self.mayIndexError |= other.mayIndexError - self.errmsg = "Expected " + str(self) + self.errmsg = "Expected " + _ustr(self) return self @@ -2371,9 +3232,19 @@ class ParseExpression(ParserElement): return ret class And(ParseExpression): - """Requires all given C{ParseExpression}s to be found in the given order. - Expressions may be separated by whitespace. - May be constructed using the C{'+'} operator. + """ + Requires all given C{ParseExpression}s to be found in the given order. + Expressions may be separated by whitespace. + May be constructed using the C{'+'} operator. + May also be constructed using the C{'-'} operator, which will suppress backtracking. + + Example:: + integer = Word(nums) + name_expr = OneOrMore(Word(alphas)) + + expr = And([integer("id"),name_expr("name"),integer("age")]) + # more easily written as: + expr = integer("id") + name_expr("name") + integer("age") """ class _ErrorStop(Empty): @@ -2405,9 +3276,9 @@ class And(ParseExpression): raise except ParseBaseException as pe: pe.__traceback__ = None - raise ParseSyntaxException(pe) + raise ParseSyntaxException._from_exception(pe) except IndexError: - raise ParseSyntaxException( ParseException(instring, len(instring), self.errmsg, self) ) + raise ParseSyntaxException(instring, len(instring), self.errmsg, self) else: loc, exprtokens = e._parse( instring, loc, doActions ) if exprtokens or exprtokens.haskeys(): @@ -2416,7 +3287,7 @@ class And(ParseExpression): def __iadd__(self, other ): if isinstance( other, basestring ): - other = Literal( other ) + other = ParserElement._literalStringClass( other ) return self.append( other ) #And( [ self, other ] ) def checkRecursion( self, parseElementList ): @@ -2437,9 +3308,18 @@ class And(ParseExpression): class Or(ParseExpression): - """Requires that at least one C{ParseExpression} is found. - If two expressions match, the expression that matches the longest string will be used. - May be constructed using the C{'^'} operator. + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the expression that matches the longest string will be used. + May be constructed using the C{'^'} operator. + + Example:: + # construct Or using '^' operator + + number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) + prints:: + [['123'], ['3.1416'], ['789']] """ def __init__( self, exprs, savelist = False ): super(Or,self).__init__(exprs, savelist) @@ -2488,7 +3368,7 @@ class Or(ParseExpression): def __ixor__(self, other ): if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) return self.append( other ) #Or( [ self, other ] ) def __str__( self ): @@ -2507,9 +3387,21 @@ class Or(ParseExpression): class MatchFirst(ParseExpression): - """Requires that at least one C{ParseExpression} is found. - If two expressions match, the first one listed is the one that will match. - May be constructed using the C{'|'} operator. + """ + Requires that at least one C{ParseExpression} is found. + If two expressions match, the first one listed is the one that will match. + May be constructed using the C{'|'} operator. + + Example:: + # construct MatchFirst using '|' operator + + # watch the order of expressions to match + number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) + print(number.searchString("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] + + # put more selective expression first + number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) + print(number.searchString("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] """ def __init__( self, exprs, savelist = False ): super(MatchFirst,self).__init__(exprs, savelist) @@ -2544,7 +3436,7 @@ class MatchFirst(ParseExpression): def __ior__(self, other ): if isinstance( other, basestring ): - other = ParserElement.literalStringClass( other ) + other = ParserElement._literalStringClass( other ) return self.append( other ) #MatchFirst( [ self, other ] ) def __str__( self ): @@ -2563,9 +3455,58 @@ class MatchFirst(ParseExpression): class Each(ParseExpression): - """Requires all given C{ParseExpression}s to be found, but in any order. - Expressions may be separated by whitespace. - May be constructed using the C{'&'} operator. + """ + Requires all given C{ParseExpression}s to be found, but in any order. + Expressions may be separated by whitespace. + May be constructed using the C{'&'} operator. + + Example:: + color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") + shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") + integer = Word(nums) + shape_attr = "shape:" + shape_type("shape") + posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") + color_attr = "color:" + color("color") + size_attr = "size:" + integer("size") + + # use Each (using operator '&') to accept attributes in any order + # (shape and posn are required, color and size are optional) + shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr) + + shape_spec.runTests(''' + shape: SQUARE color: BLACK posn: 100, 120 + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + color:GREEN size:20 shape:TRIANGLE posn:20,40 + ''' + ) + prints:: + shape: SQUARE color: BLACK posn: 100, 120 + ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] + - color: BLACK + - posn: ['100', ',', '120'] + - x: 100 + - y: 120 + - shape: SQUARE + + + shape: CIRCLE size: 50 color: BLUE posn: 50,80 + ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] + - color: BLUE + - posn: ['50', ',', '80'] + - x: 50 + - y: 80 + - shape: CIRCLE + - size: 50 + + + color: GREEN size: 20 shape: TRIANGLE posn: 20,40 + ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] + - color: GREEN + - posn: ['20', ',', '40'] + - x: 20 + - y: 40 + - shape: TRIANGLE + - size: 20 """ def __init__( self, exprs, savelist = True ): super(Each,self).__init__(exprs, savelist) @@ -2619,17 +3560,7 @@ class Each(ParseExpression): loc,results = e._parse(instring,loc,doActions) resultlist.append(results) - finalResults = ParseResults([]) - for r in resultlist: - dups = {} - for k in r.keys(): - if k in finalResults: - tmp = ParseResults(finalResults[k]) - tmp += ParseResults(r[k]) - dups[k] = tmp - finalResults += ParseResults(r) - for k,v in dups.items(): - finalResults[k] = v + finalResults = sum(resultlist, ParseResults([])) return loc, finalResults def __str__( self ): @@ -2648,11 +3579,16 @@ class Each(ParseExpression): class ParseElementEnhance(ParserElement): - """Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.""" + """ + Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens. + """ def __init__( self, expr, savelist=False ): super(ParseElementEnhance,self).__init__(savelist) if isinstance( expr, basestring ): - expr = Literal(expr) + if issubclass(ParserElement._literalStringClass, Token): + expr = ParserElement._literalStringClass(expr) + else: + expr = ParserElement._literalStringClass(Literal(expr)) self.expr = expr self.strRepr = None if expr is not None: @@ -2720,10 +3656,22 @@ class ParseElementEnhance(ParserElement): class FollowedBy(ParseElementEnhance): - """Lookahead matching of the given parse expression. C{FollowedBy} - does *not* advance the parsing position within the input string, it only + """ + Lookahead matching of the given parse expression. C{FollowedBy} + does I{not} advance the parsing position within the input string, it only verifies that the specified parse expression matches at the current - position. C{FollowedBy} always returns a null token list.""" + position. C{FollowedBy} always returns a null token list. + + Example:: + # use FollowedBy to match a label only if it is followed by a ':' + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint() + prints:: + [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] + """ def __init__( self, expr ): super(FollowedBy,self).__init__(expr) self.mayReturnEmpty = True @@ -2734,11 +3682,16 @@ class FollowedBy(ParseElementEnhance): class NotAny(ParseElementEnhance): - """Lookahead to disallow matching with the given parse expression. C{NotAny} - does *not* advance the parsing position within the input string, it only - verifies that the specified parse expression does *not* match at the current - position. Also, C{NotAny} does *not* skip over leading whitespace. C{NotAny} - always returns a null token list. May be constructed using the '~' operator.""" + """ + Lookahead to disallow matching with the given parse expression. C{NotAny} + does I{not} advance the parsing position within the input string, it only + verifies that the specified parse expression does I{not} match at the current + position. Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny} + always returns a null token list. May be constructed using the '~' operator. + + Example:: + + """ def __init__( self, expr ): super(NotAny,self).__init__(expr) #~ self.leaveWhitespace() @@ -2747,11 +3700,7 @@ class NotAny(ParseElementEnhance): self.errmsg = "Found unwanted token, "+_ustr(self.expr) def parseImpl( self, instring, loc, doActions=True ): - try: - self.expr.tryParse( instring, loc ) - except (ParseException,IndexError): - pass - else: + if self.expr.canParseNext(instring, loc): raise ParseException(instring, loc, self.errmsg, self) return loc, [] @@ -2764,80 +3713,114 @@ class NotAny(ParseElementEnhance): return self.strRepr - -class ZeroOrMore(ParseElementEnhance): - """Optional repetition of zero or more of the given expression.""" - def __init__( self, expr ): - super(ZeroOrMore,self).__init__(expr) - self.mayReturnEmpty = True +class _MultipleMatch(ParseElementEnhance): + def __init__( self, expr, stopOn=None): + super(_MultipleMatch, self).__init__(expr) + ender = stopOn + if isinstance(ender, basestring): + ender = ParserElement._literalStringClass(ender) + self.not_ender = ~ender if ender is not None else None def parseImpl( self, instring, loc, doActions=True ): - tokens = [] + self_expr_parse = self.expr._parse + self_skip_ignorables = self._skipIgnorables + check_ender = self.not_ender is not None + if check_ender: + try_not_ender = self.not_ender.tryParse + + # must be at least one (but first see if we are the stopOn sentinel; + # if so, fail) + if check_ender: + try_not_ender(instring, loc) + loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False ) try: - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) + hasIgnoreExprs = (not not self.ignoreExprs) while 1: + if check_ender: + try_not_ender(instring, loc) if hasIgnoreExprs: - preloc = self._skipIgnorables( instring, loc ) + preloc = self_skip_ignorables( instring, loc ) else: preloc = loc - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) + loc, tmptokens = self_expr_parse( instring, preloc, doActions ) if tmptokens or tmptokens.haskeys(): tokens += tmptokens except (ParseException,IndexError): pass return loc, tokens + +class OneOrMore(_MultipleMatch): + """ + Repetition of one or more of the given expression. + + Parameters: + - expr - expression that must match one or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: BLACK" + OneOrMore(attr_expr).parseString(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] + + # use stopOn attribute for OneOrMore to avoid reading label string as part of the data + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] + + # could also be written as + (attr_expr * (1,)).parseString(text).pprint() + """ def __str__( self ): if hasattr(self,"name"): return self.name if self.strRepr is None: - self.strRepr = "[" + _ustr(self.expr) + "]..." + self.strRepr = "{" + _ustr(self.expr) + "}..." return self.strRepr def setResultsName( self, name, listAllMatches=False ): - ret = super(ZeroOrMore,self).setResultsName(name,listAllMatches) + ret = super(OneOrMore,self).setResultsName(name,listAllMatches) ret.saveAsList = True return ret +class ZeroOrMore(_MultipleMatch): + """ + Optional repetition of zero or more of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - stopOn - (default=C{None}) - expression for a terminating sentinel + (only required if the sentinel would ordinarily match the repetition + expression) -class OneOrMore(ParseElementEnhance): - """Repetition of one or more of the given expression.""" + Example: similar to L{OneOrMore} + """ + def __init__( self, expr, stopOn=None): + super(ZeroOrMore,self).__init__(expr, stopOn=stopOn) + self.mayReturnEmpty = True + def parseImpl( self, instring, loc, doActions=True ): - # must be at least one - loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False ) try: - hasIgnoreExprs = ( len(self.ignoreExprs) > 0 ) - while 1: - if hasIgnoreExprs: - preloc = self._skipIgnorables( instring, loc ) - else: - preloc = loc - loc, tmptokens = self.expr._parse( instring, preloc, doActions ) - if tmptokens or tmptokens.haskeys(): - tokens += tmptokens + return super(ZeroOrMore, self).parseImpl(instring, loc, doActions) except (ParseException,IndexError): - pass - - return loc, tokens + return loc, [] def __str__( self ): if hasattr(self,"name"): return self.name if self.strRepr is None: - self.strRepr = "{" + _ustr(self.expr) + "}..." + self.strRepr = "[" + _ustr(self.expr) + "]..." return self.strRepr - def setResultsName( self, name, listAllMatches=False ): - ret = super(OneOrMore,self).setResultsName(name,listAllMatches) - ret.saveAsList = True - return ret - class _NullToken(object): def __bool__(self): return False @@ -2847,9 +3830,39 @@ class _NullToken(object): _optionalNotMatched = _NullToken() class Optional(ParseElementEnhance): - """Optional matching of the given expression. - A default return string can also be specified, if the optional expression - is not found. + """ + Optional matching of the given expression. + + Parameters: + - expr - expression that must match zero or more times + - default (optional) - value to be returned if the optional expression is not found. + + Example:: + # US postal code can be a 5-digit zip, plus optional 4-digit qualifier + zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4))) + zip.runTests(''' + # traditional ZIP code + 12345 + + # ZIP+4 form + 12101-0001 + + # invalid ZIP + 98765- + ''') + prints:: + # traditional ZIP code + 12345 + ['12345'] + + # ZIP+4 form + 12101-0001 + ['12101-0001'] + + # invalid ZIP + 98765- + ^ + FAIL: Expected end of text (at char 5), (line:1, col:6) """ def __init__( self, expr, default=_optionalNotMatched ): super(Optional,self).__init__( expr, savelist=False ) @@ -2879,13 +3892,60 @@ class Optional(ParseElementEnhance): return self.strRepr - class SkipTo(ParseElementEnhance): - """Token for skipping over all undefined text until the matched expression is found. - If C{include} is set to true, the matched expression is also parsed (the skipped text - and matched expression are returned as a 2-element list). The C{ignore} - argument is used to define grammars (typically quoted strings and comments) that - might contain false matches. + """ + Token for skipping over all undefined text until the matched expression is found. + + Parameters: + - expr - target expression marking the end of the data to be skipped + - include - (default=C{False}) if True, the target expression is also parsed + (the skipped text and target expression are returned as a 2-element list). + - ignore - (default=C{None}) used to define grammars (typically quoted strings and + comments) that might contain false matches to the target expression + - failOn - (default=C{None}) define expressions that are not allowed to be + included in the skipped test; if found before the target expression is found, + the SkipTo is not a match + + Example:: + report = ''' + Outstanding Issues Report - 1 Jan 2000 + + # | Severity | Description | Days Open + -----+----------+-------------------------------------------+----------- + 101 | Critical | Intermittent system crash | 6 + 94 | Cosmetic | Spelling error on Login ('log|n') | 14 + 79 | Minor | System slow when running too many reports | 47 + ''' + integer = Word(nums) + SEP = Suppress('|') + # use SkipTo to simply match everything up until the next SEP + # - ignore quoted strings, so that a '|' character inside a quoted string does not match + # - parse action will call token.strip() for each matched token, i.e., the description body + string_data = SkipTo(SEP, ignore=quotedString) + string_data.setParseAction(tokenMap(str.strip)) + ticket_expr = (integer("issue_num") + SEP + + string_data("sev") + SEP + + string_data("desc") + SEP + + integer("days_open")) + + for tkt in ticket_expr.searchString(report): + print tkt.dump() + prints:: + ['101', 'Critical', 'Intermittent system crash', '6'] + - days_open: 6 + - desc: Intermittent system crash + - issue_num: 101 + - sev: Critical + ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] + - days_open: 14 + - desc: Spelling error on Login ('log|n') + - issue_num: 94 + - sev: Cosmetic + ['79', 'Minor', 'System slow when running too many reports', '47'] + - days_open: 47 + - desc: System slow when running too many reports + - issue_num: 79 + - sev: Minor """ def __init__( self, other, include=False, ignore=None, failOn=None ): super( SkipTo, self ).__init__( other ) @@ -2894,77 +3954,85 @@ class SkipTo(ParseElementEnhance): self.mayIndexError = False self.includeMatch = include self.asList = False - if failOn is not None and isinstance(failOn, basestring): - self.failOn = Literal(failOn) + if isinstance(failOn, basestring): + self.failOn = ParserElement._literalStringClass(failOn) else: self.failOn = failOn self.errmsg = "No match found for "+_ustr(self.expr) def parseImpl( self, instring, loc, doActions=True ): - startLoc = loc + startloc = loc instrlen = len(instring) expr = self.expr - failParse = False - while loc <= instrlen: - try: - if self.failOn: + expr_parse = self.expr._parse + self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None + self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None + + tmploc = loc + while tmploc <= instrlen: + if self_failOn_canParseNext is not None: + # break if failOn expression matches + if self_failOn_canParseNext(instring, tmploc): + break + + if self_ignoreExpr_tryParse is not None: + # advance past ignore expressions + while 1: try: - self.failOn.tryParse(instring, loc) + tmploc = self_ignoreExpr_tryParse(instring, tmploc) except ParseBaseException: - pass - else: - failParse = True - raise ParseException(instring, loc, "Found expression " + str(self.failOn)) - failParse = False - if self.ignoreExpr is not None: - while 1: - try: - loc = self.ignoreExpr.tryParse(instring,loc) - # print("found ignoreExpr, advance to", loc) - except ParseBaseException: - break - expr._parse( instring, loc, doActions=False, callPreParse=False ) - skipText = instring[startLoc:loc] - if self.includeMatch: - loc,mat = expr._parse(instring,loc,doActions,callPreParse=False) - if mat: - skipRes = ParseResults( skipText ) - skipRes += mat - return loc, [ skipRes ] - else: - return loc, [ skipText ] - else: - return loc, [ skipText ] - except (ParseException,IndexError): - if failParse: - raise - else: - loc += 1 - raise ParseException(instring, loc, self.errmsg, self) + break + + try: + expr_parse(instring, tmploc, doActions=False, callPreParse=False) + except (ParseException, IndexError): + # no match, advance loc in string + tmploc += 1 + else: + # matched skipto expr, done + break + + else: + # ran off the end of the input string without matching skipto expr, fail + raise ParseException(instring, loc, self.errmsg, self) + + # build up return values + loc = tmploc + skiptext = instring[startloc:loc] + skipresult = ParseResults(skiptext) + + if self.includeMatch: + loc, mat = expr_parse(instring,loc,doActions,callPreParse=False) + skipresult += mat + + return loc, skipresult class Forward(ParseElementEnhance): - """Forward declaration of an expression to be defined later - - used for recursive grammars, such as algebraic infix notation. - When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. - - Note: take care when assigning to C{Forward} not to overlook precedence of operators. - Specifically, '|' has a lower precedence than '<<', so that:: - fwdExpr << a | b | c - will actually be evaluated as:: - (fwdExpr << a) | b | c - thereby leaving b and c out as parseable alternatives. It is recommended that you - explicitly group the values inserted into the C{Forward}:: - fwdExpr << (a | b | c) - Converting to use the '<<=' operator instead will avoid this problem. + """ + Forward declaration of an expression to be defined later - + used for recursive grammars, such as algebraic infix notation. + When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator. + + Note: take care when assigning to C{Forward} not to overlook precedence of operators. + Specifically, '|' has a lower precedence than '<<', so that:: + fwdExpr << a | b | c + will actually be evaluated as:: + (fwdExpr << a) | b | c + thereby leaving b and c out as parseable alternatives. It is recommended that you + explicitly group the values inserted into the C{Forward}:: + fwdExpr << (a | b | c) + Converting to use the '<<=' operator instead will avoid this problem. + + See L{ParseResults.pprint} for an example of a recursive parser created using + C{Forward}. """ def __init__( self, other=None ): super(Forward,self).__init__( other, savelist=False ) def __lshift__( self, other ): if isinstance( other, basestring ): - other = ParserElement.literalStringClass(other) + other = ParserElement._literalStringClass(other) self.expr = other - self.mayReturnEmpty = other.mayReturnEmpty self.strRepr = None self.mayIndexError = self.expr.mayIndexError self.mayReturnEmpty = self.expr.mayReturnEmpty @@ -2998,7 +4066,9 @@ class Forward(ParseElementEnhance): def __str__( self ): if hasattr(self,"name"): return self.name + return self.__class__.__name__ + ": ..." + # stubbed out for now - creates awful memory and perf issues self._revertClass = self.__class__ self.__class__ = _ForwardNoRecurse try: @@ -3023,26 +4093,29 @@ class _ForwardNoRecurse(Forward): return "..." class TokenConverter(ParseElementEnhance): - """Abstract subclass of C{ParseExpression}, for converting parsed results.""" + """ + Abstract subclass of C{ParseExpression}, for converting parsed results. + """ def __init__( self, expr, savelist=False ): super(TokenConverter,self).__init__( expr )#, savelist ) self.saveAsList = False -class Upcase(TokenConverter): - """Converter to upper case all matching tokens.""" - def __init__(self, *args): - super(Upcase,self).__init__(*args) - warnings.warn("Upcase class is deprecated, use upcaseTokens parse action instead", - DeprecationWarning,stacklevel=2) - - def postParse( self, instring, loc, tokenlist ): - return list(map( str.upper, tokenlist )) - - class Combine(TokenConverter): - """Converter to concatenate all matching tokens to a single string. - By default, the matching patterns must also be contiguous in the input string; - this can be disabled by specifying C{'adjacent=False'} in the constructor. + """ + Converter to concatenate all matching tokens to a single string. + By default, the matching patterns must also be contiguous in the input string; + this can be disabled by specifying C{'adjacent=False'} in the constructor. + + Example:: + real = Word(nums) + '.' + Word(nums) + print(real.parseString('3.1416')) # -> ['3', '.', '1416'] + # will also erroneously match the following + print(real.parseString('3. 1416')) # -> ['3', '.', '1416'] + + real = Combine(Word(nums) + '.' + Word(nums)) + print(real.parseString('3.1416')) # -> ['3.1416'] + # no match when there are internal spaces + print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...) """ def __init__( self, expr, joinString="", adjacent=True ): super(Combine,self).__init__( expr ) @@ -3072,7 +4145,19 @@ class Combine(TokenConverter): return retToks class Group(TokenConverter): - """Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.""" + """ + Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions. + + Example:: + ident = Word(alphas) + num = Word(nums) + term = ident | num + func = ident + Optional(delimitedList(term)) + print(func.parseString("fn a,b,100")) # -> ['fn', 'a', 'b', '100'] + + func = ident + Group(Optional(delimitedList(term))) + print(func.parseString("fn a,b,100")) # -> ['fn', ['a', 'b', '100']] + """ def __init__( self, expr ): super(Group,self).__init__( expr ) self.saveAsList = True @@ -3081,9 +4166,40 @@ class Group(TokenConverter): return [ tokenlist ] class Dict(TokenConverter): - """Converter to return a repetitive expression as a list, but also as a dictionary. - Each element can also be referenced using the first token in the expression as its key. - Useful for tabular report scraping when the first column can be used as a item key. + """ + Converter to return a repetitive expression as a list, but also as a dictionary. + Each element can also be referenced using the first token in the expression as its key. + Useful for tabular report scraping when the first column can be used as a item key. + + Example:: + data_word = Word(alphas) + label = data_word + FollowedBy(':') + attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join)) + + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + + # print attributes as plain groups + print(OneOrMore(attr_expr).parseString(text).dump()) + + # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names + result = Dict(OneOrMore(Group(attr_expr))).parseString(text) + print(result.dump()) + + # access named fields as dict entries, or output as dict + print(result['shape']) + print(result.asDict()) + prints:: + ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] + + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} + See more examples at L{ParseResults} of accessing fields by results name. """ def __init__( self, expr ): super(Dict,self).__init__( expr ) @@ -3115,7 +4231,24 @@ class Dict(TokenConverter): class Suppress(TokenConverter): - """Converter for ignoring the results of a parsed expression.""" + """ + Converter for ignoring the results of a parsed expression. + + Example:: + source = "a, b, c,d" + wd = Word(alphas) + wd_list1 = wd + ZeroOrMore(',' + wd) + print(wd_list1.parseString(source)) + + # often, delimiters that are useful during parsing are just in the + # way afterward - use Suppress to keep them out of the parsed output + wd_list2 = wd + ZeroOrMore(Suppress(',') + wd) + print(wd_list2.parseString(source)) + prints:: + ['a', ',', 'b', ',', 'c', ',', 'd'] + ['a', 'b', 'c', 'd'] + (See also L{delimitedList}.) + """ def postParse( self, instring, loc, tokenlist ): return [] @@ -3124,7 +4257,9 @@ class Suppress(TokenConverter): class OnlyOnce(object): - """Wrapper for parse actions, to ensure they are only called once.""" + """ + Wrapper for parse actions, to ensure they are only called once. + """ def __init__(self, methodCall): self.callable = _trim_arity(methodCall) self.called = False @@ -3138,20 +4273,36 @@ class OnlyOnce(object): self.called = False def traceParseAction(f): - """Decorator for debugging parse actions.""" + """ + Decorator for debugging parse actions. + + Example:: + wd = Word(alphas) + + @traceParseAction + def remove_duplicate_chars(tokens): + return ''.join(sorted(set(''.join(tokens))) + + wds = OneOrMore(wd).setParseAction(remove_duplicate_chars) + print(wds.parseString("slkdjs sld sldd sdlf sdljf")) + prints:: + >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) + <3: thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc - sys.stderr.write( ">>entering %s(line: '%s', %d, %s)\n" % (thisFunc,line(l,s),l,t) ) + sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) ) try: ret = f(*paArgs) except Exception as exc: sys.stderr.write( "< ['aa', 'bb', 'cc'] + delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] """ dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..." if combine: @@ -3177,11 +4333,15 @@ def delimitedList( expr, delim=",", combine=False ): return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName) def countedArray( expr, intExpr=None ): - """Helper to define a counted list of expressions. - This helper defines a pattern of the form:: - integer expr expr expr... - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + """ + Helper to define a counted list of expressions. + This helper defines a pattern of the form:: + integer expr expr expr... + where the leading integer tells how many expr expressions follow. + The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + + Example:: + countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] """ arrayExpr = Forward() def countFieldParseAction(s,l,t): @@ -3194,7 +4354,7 @@ def countedArray( expr, intExpr=None ): intExpr = intExpr.copy() intExpr.setName("arrayLen") intExpr.addParseAction(countFieldParseAction, callDuringTry=True) - return ( intExpr + arrayExpr ) + return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...') def _flatten(L): ret = [] @@ -3206,16 +4366,17 @@ def _flatten(L): return ret def matchPreviousLiteral(expr): - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks - for a 'repeat' of a previous expression. For example:: - first = Word(nums) - second = matchPreviousLiteral(first) - matchExpr = first + ":" + second - will match C{"1:1"}, but not C{"1:2"}. Because this matches a - previous literal, will also match the leading C{"1:1"} in C{"1:10"}. - If this is not desired, use C{matchPreviousExpr}. - Do *not* use with packrat parsing enabled. + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousLiteral(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches a + previous literal, will also match the leading C{"1:1"} in C{"1:10"}. + If this is not desired, use C{matchPreviousExpr}. + Do I{not} use with packrat parsing enabled. """ rep = Forward() def copyTokenToRepeater(s,l,t): @@ -3225,24 +4386,26 @@ def matchPreviousLiteral(expr): else: # flatten t tokens tflat = _flatten(t.asList()) - rep << And( [ Literal(tt) for tt in tflat ] ) + rep << And(Literal(tt) for tt in tflat) else: rep << Empty() expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) return rep def matchPreviousExpr(expr): - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks - for a 'repeat' of a previous expression. For example:: - first = Word(nums) - second = matchPreviousExpr(first) - matchExpr = first + ":" + second - will match C{"1:1"}, but not C{"1:2"}. Because this matches by - expressions, will *not* match the leading C{"1:1"} in C{"1:10"}; - the expressions are evaluated first, and then compared, so - C{"1"} is compared with C{"10"}. - Do *not* use with packrat parsing enabled. + """ + Helper to define an expression that is indirectly defined from + the tokens matched in a previous expression, that is, it looks + for a 'repeat' of a previous expression. For example:: + first = Word(nums) + second = matchPreviousExpr(first) + matchExpr = first + ":" + second + will match C{"1:1"}, but not C{"1:2"}. Because this matches by + expressions, will I{not} match the leading C{"1:1"} in C{"1:10"}; + the expressions are evaluated first, and then compared, so + C{"1"} is compared with C{"10"}. + Do I{not} use with packrat parsing enabled. """ rep = Forward() e2 = expr.copy() @@ -3255,6 +4418,7 @@ def matchPreviousExpr(expr): raise ParseException("",0,"") rep.setParseAction( mustMatchTheseTokens, callDuringTry=True ) expr.addParseAction(copyTokenToRepeater, callDuringTry=True) + rep.setName('(prev) ' + _ustr(expr)) return rep def _escapeRegexRangeChars(s): @@ -3266,16 +4430,27 @@ def _escapeRegexRangeChars(s): return _ustr(s) def oneOf( strs, caseless=False, useRegex=True ): - """Helper to quickly define a set of alternative Literals, and makes sure to do - longest-first testing when there is a conflict, regardless of the input order, - but returns a C{L{MatchFirst}} for best performance. - - Parameters: - - strs - a string of space-delimited literals, or a list of string literals - - caseless - (default=False) - treat all literals as caseless - - useRegex - (default=True) - as an optimization, will generate a Regex + """ + Helper to quickly define a set of alternative Literals, and makes sure to do + longest-first testing when there is a conflict, regardless of the input order, + but returns a C{L{MatchFirst}} for best performance. + + Parameters: + - strs - a string of space-delimited literals, or a collection of string literals + - caseless - (default=C{False}) - treat all literals as caseless + - useRegex - (default=C{True}) - as an optimization, will generate a Regex object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or if creating a C{Regex} raises an exception) + + Example:: + comp_oper = oneOf("< = > <= >= !=") + var = Word(alphas) + number = Word(nums) + term = var | number + comparison_expr = term + comp_oper + term + print(comparison_expr.searchString("B = 12 AA=23 B<=AA AA>12")) + prints:: + [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] """ if caseless: isequal = ( lambda a,b: a.upper() == b.upper() ) @@ -3289,12 +4464,10 @@ def oneOf( strs, caseless=False, useRegex=True ): symbols = [] if isinstance(strs,basestring): symbols = strs.split() - elif isinstance(strs, collections.Sequence): - symbols = list(strs[:]) - elif isinstance(strs, _generatorType): + elif isinstance(strs, collections.Iterable): symbols = list(strs) else: - warnings.warn("Invalid argument to oneOf, expected string or list", + warnings.warn("Invalid argument to oneOf, expected string or iterable", SyntaxWarning, stacklevel=2) if not symbols: return NoMatch() @@ -3318,41 +4491,76 @@ def oneOf( strs, caseless=False, useRegex=True ): #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] )) try: if len(symbols)==len("".join(symbols)): - return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ) + return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols)) else: - return Regex( "|".join(re.escape(sym) for sym in symbols) ) + return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols)) except: warnings.warn("Exception creating Regex for oneOf, building MatchFirst", SyntaxWarning, stacklevel=2) # last resort, just use MatchFirst - return MatchFirst( [ parseElementClass(sym) for sym in symbols ] ) + return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols)) def dictOf( key, value ): - """Helper to easily and clearly define a dictionary by specifying the respective patterns - for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens - in the proper order. The key pattern can include delimiting markers or punctuation, - as long as they are suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the C{Dict} results can include named token - fields. + """ + Helper to easily and clearly define a dictionary by specifying the respective patterns + for the key and value. Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens + in the proper order. The key pattern can include delimiting markers or punctuation, + as long as they are suppressed, thereby leaving the significant key text. The value + pattern can include named results, so that the C{Dict} results can include named token + fields. + + Example:: + text = "shape: SQUARE posn: upper left color: light blue texture: burlap" + attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)) + print(OneOrMore(attr_expr).parseString(text).dump()) + + attr_label = label + attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join) + + # similar to Dict, but simpler call format + result = dictOf(attr_label, attr_value).parseString(text) + print(result.dump()) + print(result['shape']) + print(result.shape) # object attribute access works too + print(result.asDict()) + prints:: + [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] + - color: light blue + - posn: upper left + - shape: SQUARE + - texture: burlap + SQUARE + SQUARE + {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} """ return Dict( ZeroOrMore( Group ( key + value ) ) ) def originalTextFor(expr, asString=True): - """Helper to return the original, untokenized text for a given expression. Useful to - restore the parsed fields of an HTML start tag into the raw tag text itself, or to - revert separate tokens with intervening whitespace back to the original matching - input text. Simpler to use than the parse action C{L{keepOriginalText}}, and does not - require the inspect module to chase up the call stack. By default, returns a - string containing the original parsed text. + """ + Helper to return the original, untokenized text for a given expression. Useful to + restore the parsed fields of an HTML start tag into the raw tag text itself, or to + revert separate tokens with intervening whitespace back to the original matching + input text. By default, returns astring containing the original parsed text. - If the optional C{asString} argument is passed as C{False}, then the return value is a - C{L{ParseResults}} containing any results names that were originally matched, and a - single token containing the original matched text from the input string. So if - the expression passed to C{L{originalTextFor}} contains expressions with defined - results names, you must set C{asString} to C{False} if you want to preserve those - results name values.""" + If the optional C{asString} argument is passed as C{False}, then the return value is a + C{L{ParseResults}} containing any results names that were originally matched, and a + single token containing the original matched text from the input string. So if + the expression passed to C{L{originalTextFor}} contains expressions with defined + results names, you must set C{asString} to C{False} if you want to preserve those + results name values. + + Example:: + src = "this is test bold text normal text " + for tag in ("b","i"): + opener,closer = makeHTMLTags(tag) + patt = originalTextFor(opener + SkipTo(closer) + closer) + print(patt.searchString(src)[0]) + prints:: + [' bold text '] + ['text'] + """ locMarker = Empty().setParseAction(lambda s,loc,t: loc) endlocMarker = locMarker.copy() endlocMarker.callPreparse = False @@ -3361,27 +4569,37 @@ def originalTextFor(expr, asString=True): extractText = lambda s,l,t: s[t._original_start:t._original_end] else: def extractText(s,l,t): - del t[:] - t.insert(0, s[t._original_start:t._original_end]) - del t["_original_start"] - del t["_original_end"] + t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]] matchExpr.setParseAction(extractText) + matchExpr.ignoreExprs = expr.ignoreExprs return matchExpr def ungroup(expr): - """Helper to undo pyparsing's default grouping of And expressions, even - if all but one are non-empty.""" + """ + Helper to undo pyparsing's default grouping of And expressions, even + if all but one are non-empty. + """ return TokenConverter(expr).setParseAction(lambda t:t[0]) def locatedExpr(expr): - """Helper to decorate a returned token with its starting and ending locations in the input string. - This helper adds the following results names: - - locn_start = location where matched expression begins - - locn_end = location where matched expression ends - - value = the actual parsed results - - Be careful if the input text contains C{} characters, you may want to call - C{L{ParserElement.parseWithTabs}} + """ + Helper to decorate a returned token with its starting and ending locations in the input string. + This helper adds the following results names: + - locn_start = location where matched expression begins + - locn_end = location where matched expression ends + - value = the actual parsed results + + Be careful if the input text contains C{} characters, you may want to call + C{L{ParserElement.parseWithTabs}} + + Example:: + wd = Word(alphas) + for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): + print(match) + prints:: + [[0, 'ljsdf', 5]] + [[8, 'lksdjjf', 15]] + [[18, 'lkkjj', 23]] """ locator = Empty().setParseAction(lambda s,l,t: l) return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end")) @@ -3402,21 +4620,22 @@ _charRange = Group(_singleChar + Suppress("-") + _singleChar) _reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]" def srange(s): - r"""Helper to easily define string ranges for use in Word construction. Borrows - syntax from regexp '[]' string range definitions:: - srange("[0-9]") -> "0123456789" - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - The input string must be enclosed in []'s, and the returned string is the expanded - character set joined into a single string. - The values enclosed in the []'s may be:: - a single character - an escaped character with a leading backslash (such as \- or \]) - an escaped hex character with a leading '\x' (\x21, which is a '!' character) - (\0x## is also supported for backwards compatibility) - an escaped octal character with a leading '\0' (\041, which is a '!' character) - a range of any of the above, separated by a dash ('a-z', etc.) - any combination of the above ('aeiouy', 'a-zA-Z0-9_$', etc.) + r""" + Helper to easily define string ranges for use in Word construction. Borrows + syntax from regexp '[]' string range definitions:: + srange("[0-9]") -> "0123456789" + srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" + srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" + The input string must be enclosed in []'s, and the returned string is the expanded + character set joined into a single string. + The values enclosed in the []'s may be: + - a single character + - an escaped character with a leading backslash (such as C{\-} or C{\]}) + - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) + (C{\0x##} is also supported for backwards compatibility) + - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character) + - a range of any of the above, separated by a dash (C{'a-z'}, etc.) + - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.) """ _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1)) try: @@ -3425,8 +4644,9 @@ def srange(s): return "" def matchOnlyAtCol(n): - """Helper method for defining parse actions that require matching at a specific - column in the input text. + """ + Helper method for defining parse actions that require matching at a specific + column in the input text. """ def verifyCol(strg,locn,toks): if col(locn,strg) != n: @@ -3434,57 +4654,83 @@ def matchOnlyAtCol(n): return verifyCol def replaceWith(replStr): - """Helper method for common parse actions that simply return a literal value. Especially - useful when used with C{L{transformString}()}. """ - #def _replFunc(*args): - # return [replStr] - #return _replFunc - return functools.partial(next, itertools.repeat([replStr])) + Helper method for common parse actions that simply return a literal value. Especially + useful when used with C{L{transformString}()}. + + Example:: + num = Word(nums).setParseAction(lambda toks: int(toks[0])) + na = oneOf("N/A NA").setParseAction(replaceWith(math.nan)) + term = na | num + + OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234] + """ + return lambda s,l,t: [replStr] def removeQuotes(s,l,t): - """Helper parse action for removing quotation marks from parsed quoted strings. - To use, add this parse action to quoted string using:: - quotedString.setParseAction( removeQuotes ) """ - return t[0][1:-1] + Helper parse action for removing quotation marks from parsed quoted strings. -def upcaseTokens(s,l,t): - """Helper parse action to convert tokens to upper case.""" - return [ tt.upper() for tt in map(_ustr,t) ] + Example:: + # by default, quotation marks are included in parsed results + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] -def downcaseTokens(s,l,t): - """Helper parse action to convert tokens to lower case.""" - return [ tt.lower() for tt in map(_ustr,t) ] + # use removeQuotes to strip quotation marks from parsed results + quotedString.setParseAction(removeQuotes) + quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] + """ + return t[0][1:-1] + +def tokenMap(func, *args): + """ + Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional + args are passed, they are forwarded to the given function as additional arguments after + the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the + parsed data to an integer using base 16. + + Example (compare the last to example in L{ParserElement.transformString}:: + hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16)) + hex_ints.runTests(''' + 00 11 22 aa FF 0a 0d 1a + ''') + + upperword = Word(alphas).setParseAction(tokenMap(str.upper)) + OneOrMore(upperword).runTests(''' + my kingdom for a horse + ''') + + wd = Word(alphas).setParseAction(tokenMap(str.title)) + OneOrMore(wd).setParseAction(' '.join).runTests(''' + now is the winter of our discontent made glorious summer by this sun of york + ''') + prints:: + 00 11 22 aa FF 0a 0d 1a + [0, 17, 34, 170, 255, 10, 13, 26] + + my kingdom for a horse + ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] + + now is the winter of our discontent made glorious summer by this sun of york + ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] + """ + def pa(s,l,t): + return [func(tokn, *args) for tokn in t] -def keepOriginalText(s,startLoc,t): - """DEPRECATED - use new helper method C{L{originalTextFor}}. - Helper parse action to preserve original parsed text, - overriding any nested parse actions.""" - try: - endloc = getTokensEndLoc() - except ParseException: - raise ParseFatalException("incorrect usage of keepOriginalText - may only be called as a parse action") - del t[:] - t += ParseResults(s[startLoc:endloc]) - return t - -def getTokensEndLoc(): - """Method to be called from within a parse action to determine the end - location of the parsed tokens.""" - import inspect - fstack = inspect.stack() try: - # search up the stack (through intervening argument normalizers) for correct calling routine - for f in fstack[2:]: - if f[3] == "_parseNoCache": - endloc = f[0].f_locals["loc"] - return endloc - else: - raise ParseFatalException("incorrect usage of getTokensEndLoc - may only be called from within a parse action") - finally: - del fstack + func_name = getattr(func, '__name__', + getattr(func, '__class__').__name__) + except Exception: + func_name = str(func) + pa.__name__ = func_name + return pa + +upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) +"""Helper parse action to convert tokens to upper case.""" + +downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) +"""Helper parse action to convert tokens to lower case.""" + def _makeTags(tagStr, xml): """Internal helper to construct opening and closing tag expressions, given a tag name""" if isinstance(tagStr,basestring): @@ -3508,40 +4754,90 @@ def _makeTags(tagStr, xml): Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">") closeTag = Combine(_L("") - openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % tagStr) - closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % tagStr) + openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname) + closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname) openTag.tag = resname closeTag.tag = resname return openTag, closeTag def makeHTMLTags(tagStr): - """Helper to construct opening and closing tag expressions for HTML, given a tag name""" + """ + Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches + tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values. + + Example:: + text = 'More info at the pyparsing wiki page' + # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple + a,a_end = makeHTMLTags("A") + link_expr = a + SkipTo(a_end)("link_text") + a_end + + for link in link_expr.searchString(text): + # attributes in the tag (like "href" shown here) are also accessible as named results + print(link.link_text, '->', link.href) + prints:: + pyparsing -> http://pyparsing.wikispaces.com + """ return _makeTags( tagStr, False ) def makeXMLTags(tagStr): - """Helper to construct opening and closing tag expressions for XML, given a tag name""" + """ + Helper to construct opening and closing tag expressions for XML, given a tag name. Matches + tags only in the given upper/lower case. + + Example: similar to L{makeHTMLTags} + """ return _makeTags( tagStr, True ) def withAttribute(*args,**attrDict): - """Helper to create a validating parse action to be used with start tags created - with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag - with a required attribute value, to avoid false matches on common tags such as - C{} or C{
}. - - Call C{withAttribute} with a series of attribute names and values. Specify the list - of filter attributes names and values as: - - keyword arguments, as in C{(align="right")}, or - - as an explicit dict with C{**} operator, when an attribute name is also a Python + """ + Helper to create a validating parse action to be used with start tags created + with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag + with a required attribute value, to avoid false matches on common tags such as + C{} or C{
}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python reserved word, as in C{**{"class":"Customer", "align":"right"}} - - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) - For attribute names with a namespace prefix, you must use the second form. Attribute - names are matched insensitive to upper/lower case. + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. - If just testing for C{class} (with or without a namespace), use C{L{withClass}}. - - To verify that the attribute exists, but without specifying a value, pass - C{withAttribute.ANY_VALUE} as the value. - """ + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ if args: attrs = args[:] else: @@ -3558,9 +4854,37 @@ def withAttribute(*args,**attrDict): withAttribute.ANY_VALUE = object() def withClass(classname, namespace=''): - """Simplified version of C{L{withAttribute}} when matching on a div class - made - difficult because C{class} is a reserved word in Python. - """ + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ classattr = "%s:class" % namespace if namespace else "class" return withAttribute(**{classattr : classname}) @@ -3569,40 +4893,69 @@ opAssoc.LEFT = object() opAssoc.RIGHT = object() def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary or - binary, left- or right-associative. Parse actions can also be attached - to operator expressions. - - Parameters: - - baseExpr - expression representing the most basic element for the nested - - opList - list of tuples, one for each operator precedence level in the - expression grammar; each tuple is of the form - (opExpr, numTerms, rightLeftAssoc, parseAction), where: - - opExpr is the pyparsing expression for the operator; - may also be a string, which will be converted to a Literal; - if numTerms is 3, opExpr is a tuple of two expressions, for the - two operators separating the 3 terms - - numTerms is the number of terms for this operator (must - be 1, 2, or 3) - - rightLeftAssoc is the indicator whether the operator is - right or left associative, using the pyparsing-defined - constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. - - parseAction is the parse action to be associated with - expressions matching this operator expression (the - parse action tuple member may be omitted) - - lpar - expression for matching left-parentheses (default=Suppress('(')) - - rpar - expression for matching right-parentheses (default=Suppress(')')) + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signedInteger + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] """ ret = Forward() lastExpr = baseExpr | ( lpar + ret + rpar ) for i,operDef in enumerate(opList): opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr if arity == 3: if opExpr is None or len(opExpr) != 2: raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") opExpr1, opExpr2 = opExpr - thisExpr = Forward()#.setName("expr%d" % i) + thisExpr = Forward().setName(termName) if rightLeftAssoc == opAssoc.LEFT: if arity == 1: matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) @@ -3636,37 +4989,77 @@ def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): raise ValueError("operator must indicate right or left associativity") if pa: matchExpr.setParseAction( pa ) - thisExpr <<= ( matchExpr | lastExpr ) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) lastExpr = thisExpr ret <<= lastExpr return ret + operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" -dblQuotedString = Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*"').setName("string enclosed in double quotes") -sglQuotedString = Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*'").setName("string enclosed in single quotes") -quotedString = Regex(r'''(?:"(?:[^"\n\r\\]|(?:"")|(?:\\x[0-9a-fA-F]+)|(?:\\.))*")|(?:'(?:[^'\n\r\\]|(?:'')|(?:\\x[0-9a-fA-F]+)|(?:\\.))*')''').setName("quotedString using single or double quotes") -unicodeString = Combine(_L('u') + quotedString.copy()) +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): - """Helper method for defining nested lists enclosed in opening and closing - delimiters ("(" and ")" are the default). - - Parameters: - - opener - opening character for a nested list (default="("); can also be a pyparsing expression - - closer - closing character for a nested list (default=")"); can also be a pyparsing expression - - content - expression for items within the nested lists (default=None) - - ignoreExpr - expression for ignoring opening and closing delimiters (default=quotedString) - - If an expression is not provided for the content argument, the nested - expression will capture all whitespace-delimited content between delimiters - as a list of separate values. - - Use the C{ignoreExpr} argument to define expressions that may contain - opening or closing characters that should not be treated as opening - or closing characters for nesting, such as quotedString or a comment - expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. - The default is L{quotedString}, but if no expressions are to be ignored, - then pass C{None} for this argument. + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] """ if opener == closer: raise ValueError("opening and closing strings cannot be the same") @@ -3697,23 +5090,86 @@ def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.cop ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) else: ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) return ret def indentedBlock(blockStatementExpr, indentStack, indent=True): - """Helper method for defining space-delimited indentation blocks, such as - those used to define block statements in Python source code. + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. - Parameters: - - blockStatementExpr - expression defining syntax of statement that + Parameters: + - blockStatementExpr - expression defining syntax of statement that is repeated within the indented block - - indentStack - list created by caller to manage indentation stack + - indentStack - list created by caller to manage indentation stack (multiple statementWithIndentedBlock expressions within a single grammar should share a common indentStack) - - indent - boolean indicating whether block must be indented beyond the + - indent - boolean indicating whether block must be indented beyond the the current level; set to False for block of left-most statements - (default=True) - - A valid block must contain at least one C{blockStatement}. + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] """ def checkPeerIndent(s,l,t): if l >= len(s): return @@ -3738,9 +5194,9 @@ def indentedBlock(blockStatementExpr, indentStack, indent=True): indentStack.pop() NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) - INDENT = Empty() + Empty().setParseAction(checkSubIndent) - PEER = Empty().setParseAction(checkPeerIndent) - UNDENT = Empty().setParseAction(checkUnindent) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') if indent: smExpr = Group( Optional(NL) + #~ FollowedBy(blockStatementExpr) + @@ -3749,57 +5205,371 @@ def indentedBlock(blockStatementExpr, indentStack, indent=True): smExpr = Group( Optional(NL) + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr + return smExpr.setName('indented block') alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") -anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:")) -commonHTMLEntity = Combine(_L("&") + oneOf("gt lt amp nbsp quot").setResultsName("entity") +";").streamline() -_htmlEntityMap = dict(zip("gt lt amp nbsp quot".split(),'><& "')) -replaceHTMLEntity = lambda t : t.entity in _htmlEntityMap and _htmlEntityMap[t.entity] or None +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) # it's easy to get these comment structures wrong - they're very common, so may as well make them available -cStyleComment = Regex(r"/\*(?:[^*]*\*+)+?/").setName("C style comment") +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" -htmlComment = Regex(r"") -restOfLine = Regex(r".*").leaveWhitespace() -dblSlashComment = Regex(r"\/\/(\\\n|.)*").setName("// comment") -cppStyleComment = Regex(r"/(?:\*(?:[^*]*\*+)+?/|/[^\n]*(?:\n[^\n]*)*?(?:(?").setName("HTML comment") +"Comment of the form C{}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + _commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + Optional( Word(" \t") + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers}, L{reals}, L{scientific notation}) + - common L{programming identifiers} + - network addresses (L{MAC}, L{IPv4}, L{IPv6}) + - ISO8601 L{dates} and L{datetime} + - L{UUID} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signedInteger = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signedInteger().setParseAction(convertToFloat) + '/' + signedInteger().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signedInteger + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + sciReal = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sciReal | real | signedInteger).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = 'More info at the
pyparsing wiki page' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) if __name__ == "__main__": - selectToken = CaselessLiteral( "select" ) - fromToken = CaselessLiteral( "from" ) - - ident = Word( alphas, alphanums + "_$" ) - columnName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - columnNameList = Group( delimitedList( columnName ) ).setName("columns") - tableName = delimitedList( ident, ".", combine=True ).setParseAction( upcaseTokens ) - tableNameList = Group( delimitedList( tableName ) ).setName("tables") - simpleSQL = ( selectToken + \ - ( '*' | columnNameList ).setResultsName( "columns" ) + \ - fromToken + \ - tableNameList.setResultsName( "tables" ) ) - - simpleSQL.runTests("""\ - SELECT * from XYZZY, ABC - select * from SYS.XYZZY - Select A from Sys.dual - Select AA,BB,CC from Sys.dual - Select A, B, C from Sys.dual - Select A, B, C from Sys.dual - Xelect A, B, C from Sys.dual - Select A, B, C frox Sys.dual - Select - Select ^^^ frox Sys.dual - Select A, B, C from Sys.dual, Table2""") + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index 46532c0a..a30a409d 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -1,3 +1,3 @@ packaging==16.7 -pyparsing==2.0.6 +pyparsing==2.1.8 six==1.10.0 -- cgit v1.2.1 From a527fa46a1bb7829af90a3bea544709995c4f1ea Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 18 Aug 2016 08:42:56 +0700 Subject: Add missing whitespace after comma. --- pkg_resources/__init__.py | 34 ++++++++--------- pkg_resources/tests/test_resources.py | 70 +++++++++++++++++------------------ 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 58214292..d94e6339 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -752,8 +752,8 @@ class WorkingSet(object): if entry is None: entry = dist.location - keys = self.entry_keys.setdefault(entry,[]) - keys2 = self.entry_keys.setdefault(dist.location,[]) + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) if not replace and dist.key in self.by_key: # ignore hidden distros return @@ -1353,7 +1353,7 @@ def get_default_cache(): # best option, should be locale-safe (('APPDATA',), None), (('USERPROFILE',), app_data), - (('HOMEDRIVE','HOMEPATH'), app_data), + (('HOMEDRIVE', 'HOMEPATH'), app_data), (('HOMEPATH',), app_data), (('HOME',), None), # 95/98/ME @@ -1392,7 +1392,7 @@ def safe_version(version): # normalize the version return str(packaging.version.Version(version)) except packaging.version.InvalidVersion: - version = version.replace(' ','.') + version = version.replace(' ', '.') return re.sub('[^A-Za-z0-9.]+', '-', version) @@ -1410,7 +1410,7 @@ def to_filename(name): Any '-' characters are currently replaced with '_'. """ - return name.replace('-','_') + return name.replace('-', '_') def invalid_marker(text): @@ -1508,7 +1508,7 @@ class NullProvider: cache[script_filename] = ( len(script_text), 0, script_text.split('\n'), script_filename ) - script_code = compile(script_text, script_filename,'exec') + script_code = compile(script_text, script_filename, 'exec') exec(script_code, namespace, namespace) def _has(self, path): @@ -1965,7 +1965,7 @@ def find_on_path(importer, path_item, only=False): if _is_unpacked_egg(path_item): yield Distribution.from_filename( path_item, metadata=PathMetadata( - path_item, os.path.join(path_item,'EGG-INFO') + path_item, os.path.join(path_item, 'EGG-INFO') ) ) else: @@ -2037,7 +2037,7 @@ def _handle_ns(packageName, path_item): module = sys.modules[packageName] = types.ModuleType(packageName) module.__path__ = [] _set_parent_ns(packageName) - elif not hasattr(module,'__path__'): + elif not hasattr(module, '__path__'): raise TypeError("Not a package:", packageName) handler = _find_adapter(_namespace_handlers, importer) subpath = handler(importer, path_item, packageName, module) @@ -2089,8 +2089,8 @@ def declare_namespace(packageName): # Track what packages are namespaces, so when new path items are added, # they can be updated - _namespace_packages.setdefault(parent,[]).append(packageName) - _namespace_packages.setdefault(packageName,[]) + _namespace_packages.setdefault(parent, []).append(packageName) + _namespace_packages.setdefault(packageName, []) for path_item in path: # Ensure all the parent's path items are reflected in the child, @@ -2104,7 +2104,7 @@ def fixup_namespace_packages(path_item, parent=None): """Ensure that previously-declared namespace packages include path_item""" _imp.acquire_lock() try: - for package in _namespace_packages.get(parent,()): + for package in _namespace_packages.get(parent, ()): subpath = _handle_ns(package, path_item) if subpath: fixup_namespace_packages(subpath, package) @@ -2482,7 +2482,7 @@ class Distribution(object): elif not evaluate_marker(marker): reqs = [] extra = safe_extra(extra) or None - dm.setdefault(extra,[]).extend(parse_requirements(reqs)) + dm.setdefault(extra, []).extend(parse_requirements(reqs)) return dm def requires(self, extras=()): @@ -2578,7 +2578,7 @@ class Distribution(object): self._get_metadata('entry_points.txt'), self ) if group is not None: - return ep_map.get(group,{}) + return ep_map.get(group, {}) return ep_map def get_entry_info(self, group, name): @@ -2682,7 +2682,7 @@ class Distribution(object): return False return True - def clone(self,**kw): + def clone(self, **kw): """Copy this distribution, substituting in any changed keyword args""" names = 'project_name version py_version platform location precedence' for attr in names.split(): @@ -2769,7 +2769,7 @@ _distributionImpl = { } -def issue_warning(*args,**kw): +def issue_warning(*args, **kw): level = 1 g = globals() try: @@ -2916,12 +2916,12 @@ def split_sections(s): # wrap up last segment yield section, content -def _mkstemp(*args,**kw): +def _mkstemp(*args, **kw): old_open = os.open try: # temporarily bypass sandboxing os.open = os_open - return tempfile.mkstemp(*args,**kw) + return tempfile.mkstemp(*args, **kw) finally: # and then put it back os.open = old_open diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index 4e7652e3..1d663b83 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -51,15 +51,15 @@ class TestDistro: assert list(ad) == ['foopkg'] # Distributions sort by version - assert [dist.version for dist in ad['FooPkg']] == ['1.4','1.3-1','1.2'] + assert [dist.version for dist in ad['FooPkg']] == ['1.4', '1.3-1', '1.2'] # Removing a distribution leaves sequence alone ad.remove(ad['FooPkg'][1]) - assert [dist.version for dist in ad['FooPkg']] == ['1.4','1.2'] + assert [dist.version for dist in ad['FooPkg']] == ['1.4', '1.2'] # And inserting adds them in order ad.add(dist_from_fn("FooPkg-1.9.egg")) - assert [dist.version for dist in ad['FooPkg']] == ['1.9','1.4','1.2'] + assert [dist.version for dist in ad['FooPkg']] == ['1.9', '1.4', '1.2'] ws = WorkingSet([]) foo12 = dist_from_fn("FooPkg-1.2-py2.4.egg") @@ -86,7 +86,7 @@ class TestDistro: ws.add(foo14) assert ad.best_match(req, ws).version == '1.4' - def checkFooPkg(self,d): + def checkFooPkg(self, d): assert d.project_name == "FooPkg" assert d.key == "foopkg" assert d.version == "1.3.post1" @@ -97,7 +97,7 @@ class TestDistro: def testDistroBasics(self): d = Distribution( "/some/path", - project_name="FooPkg",version="1.3-1",py_version="2.4",platform="win32" + project_name="FooPkg", version="1.3-1", py_version="2.4", platform="win32" ) self.checkFooPkg(d) @@ -115,7 +115,7 @@ class TestDistro: d = Distribution( "/some/path", project_name="FooPkg", py_version="2.4", platform="win32", metadata=Metadata( - ('PKG-INFO',"Metadata-Version: 1.0\nVersion: 1.3-1\n") + ('PKG-INFO', "Metadata-Version: 1.0\nVersion: 1.3-1\n") ) ) self.checkFooPkg(d) @@ -164,7 +164,7 @@ class TestDistro: ad.add(Baz) # Activation list now includes resolved dependency - assert list(ws.resolve(parse_requirements("Foo[bar]"), ad)) == [Foo,Baz] + assert list(ws.resolve(parse_requirements("Foo[bar]"), ad)) == [Foo, Baz] # Requests for conflicting versions produce VersionConflict with pytest.raises(VersionConflict) as vc: ws.resolve(parse_requirements("Foo==1.2\nFoo!=1.2"), ad) @@ -218,7 +218,7 @@ class TestDistro: quux = Distribution.from_filename("/foo_dir/quux-1.0.dist-info") ad.add(quux) res = list(ws.resolve(parse_requirements("Foo[baz]"), ad)) - assert res == [Foo,quux] + assert res == [Foo, quux] def test_marker_evaluation_with_multiple_extras(self): ad = pkg_resources.Environment([]) @@ -238,7 +238,7 @@ class TestDistro: fred = Distribution.from_filename("/foo_dir/fred-0.1.dist-info") ad.add(fred) res = list(ws.resolve(parse_requirements("Foo[baz,bar]"), ad)) - assert sorted(res) == [fred,quux,Foo] + assert sorted(res) == [fred, quux, Foo] def test_marker_evaluation_with_extras_loop(self): ad = pkg_resources.Environment([]) @@ -274,19 +274,19 @@ class TestDistro: docutils>=0.3 [fastcgi] fcgiapp>=0.1""") - self.checkRequires(d,"Twisted>=1.5") + self.checkRequires(d, "Twisted>=1.5") self.checkRequires( - d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3".split(), ["docgen"] + d, "Twisted>=1.5 ZConfig>=2.0 docutils>=0.3".split(), ["docgen"] ) self.checkRequires( - d,"Twisted>=1.5 fcgiapp>=0.1".split(), ["fastcgi"] + d, "Twisted>=1.5 fcgiapp>=0.1".split(), ["fastcgi"] ) self.checkRequires( - d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3 fcgiapp>=0.1".split(), - ["docgen","fastcgi"] + d, "Twisted>=1.5 ZConfig>=2.0 docutils>=0.3 fcgiapp>=0.1".split(), + ["docgen", "fastcgi"] ) self.checkRequires( - d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(), + d, "Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(), ["fastcgi", "docgen"] ) with pytest.raises(pkg_resources.UnknownExtra): @@ -348,7 +348,7 @@ class TestEntryPoints: def setup_method(self, method): self.dist = Distribution.from_filename( - "FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt','[x]'))) + "FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt', '[x]'))) def testBasics(self): ep = EntryPoint( @@ -405,7 +405,7 @@ class TestEntryPoints: submap_expect = dict( feature1=EntryPoint('feature1', 'somemodule', ['somefunction']), - feature2=EntryPoint('feature2', 'another.module', ['SomeClass'], ['extra1','extra2']), + feature2=EntryPoint('feature2', 'another.module', ['SomeClass'], ['extra1', 'extra2']), feature3=EntryPoint('feature3', 'this.module', extras=['something']) ) submap_str = """ @@ -423,7 +423,7 @@ class TestEntryPoints: EntryPoint.parse_group("x", ["foo=baz", "foo=bar"]) def testParseMap(self): - m = EntryPoint.parse_map({'xyz':self.submap_str}) + m = EntryPoint.parse_map({'xyz': self.submap_str}) self.checkSubMap(m['xyz']) assert list(m.keys()) == ['xyz'] m = EntryPoint.parse_map("[xyz]\n" + self.submap_str) @@ -480,7 +480,7 @@ class TestRequirements: hash(( "twisted", packaging.specifiers.SpecifierSet(">=1.2"), - frozenset(["foo","bar"]), + frozenset(["foo", "bar"]), None )) ) @@ -521,9 +521,9 @@ class TestParsing: assert list(parse_requirements('')) == [] def testYielding(self): - for inp,out in [ - ([], []), ('x',['x']), ([[]],[]), (' x\n y', ['x','y']), - (['x\n\n','y'], ['x','y']), + for inp, out in [ + ([], []), ('x', ['x']), ([[]], []), (' x\n y', ['x', 'y']), + (['x\n\n', 'y'], ['x', 'y']), ]: assert list(pkg_resources.yield_lines(inp)) == out @@ -626,9 +626,9 @@ class TestParsing: req, = parse_requirements('foo >= 1.0, < 3') def testVersionEquality(self): - def c(s1,s2): - p1, p2 = parse_version(s1),parse_version(s2) - assert p1 == p2, (s1,s2,p1,p2) + def c(s1, s2): + p1, p2 = parse_version(s1), parse_version(s2) + assert p1 == p2, (s1, s2, p1, p2) c('1.2-rc1', '1.2rc1') c('0.4', '0.4.0') @@ -642,13 +642,13 @@ class TestParsing: c('1.2.a', '1.2a') def testVersionOrdering(self): - def c(s1,s2): - p1, p2 = parse_version(s1),parse_version(s2) - assert p1 < p2, (s1,s2,p1,p2) + def c(s1, s2): + p1, p2 = parse_version(s1), parse_version(s2) + assert p1 < p2, (s1, s2, p1, p2) - c('2.1','2.1.1') - c('2a1','2b0') - c('2a1','2.1') + c('2.1', '2.1.1') + c('2a1', '2b0') + c('2a1', '2.1') c('2.3a1', '2.3') c('2.1-1', '2.1-2') c('2.1-1', '2.1.1') @@ -660,8 +660,8 @@ class TestParsing: c('0.4', '4.0') c('0.0.4', '0.4.0') c('0post1', '0.4post1') - c('2.1.0-rc1','2.1.0') - c('2.1dev','2.1a0') + c('2.1.0-rc1', '2.1.0') + c('2.1dev', '2.1a0') torture = """ 0.80.1-3 0.80.1-2 0.80.1-1 0.79.9999+0.80.0pre4-1 @@ -669,9 +669,9 @@ class TestParsing: 0.77.2-1 0.77.1-1 0.77.0-1 """.split() - for p,v1 in enumerate(torture): + for p, v1 in enumerate(torture): for v2 in torture[p + 1:]: - c(v2,v1) + c(v2, v1) def testVersionBuildout(self): """ -- cgit v1.2.1 From 10bf8e72f80925d924abfa6d635ad738233ae79c Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 18 Aug 2016 09:12:15 +0700 Subject: Fix spacing after comment hash. --- pkg_resources/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 58214292..17b69727 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2976,7 +2976,7 @@ def _initialize_master_working_set(): # ensure that all distributions added to the working set in the future # (e.g. by calling ``require()``) will get activated as well, # with higher priority (replace=True). - dist = None # ensure dist is defined for del dist below + dist = None # ensure dist is defined for del dist below for dist in working_set: dist.activate(replace=False) del dist -- cgit v1.2.1 From 8d1eecaef52a1baf2b9fc6c772880d8c537b562f Mon Sep 17 00:00:00 2001 From: "J. Goutin" Date: Thu, 18 Aug 2016 13:42:07 +0200 Subject: Add numpy version check --- setuptools/msvc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 360c1a68..bffaa6aa 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -6,6 +6,7 @@ import sys import platform import itertools import distutils.errors +from distutils.version import StrictVersion from setuptools.extern.six.moves import filterfalse @@ -228,9 +229,9 @@ def msvc14_gen_lib_options(*args, **kwargs): """ if "numpy.distutils" in sys.modules: import numpy as np - return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) - else: - return unpatched['msvc14_gen_lib_options'](*args, **kwargs) + if StrictVersion(np.__version__) < StrictVersion('1.11.2'): + return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) + return unpatched['msvc14_gen_lib_options'](*args, **kwargs) def _augment_exception(exc, version, arch=''): -- cgit v1.2.1 From 643f0ae4b4330f0cacec3654a7cb1e94a9316618 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 15:57:20 -0400 Subject: Update changelog --- CHANGES.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9f098df9..b47465de 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,15 +2,16 @@ CHANGES ======= -v25.3.1 +v25.3.0 ------- * #739 Fix unquoted libpaths by fixing compatibility between `numpy.distutils` and `distutils._msvccompiler` for numpy < 1.11.2 (Fix issue #728, error also fixed in Numpy). -v25.3.0 -------- +* #731: Bump certifi. + +* Style updates. See #740, #741, #743, #744, #742, #747. -#731: Bump certifi. +* #735: include license file. v25.2.0 ------- -- cgit v1.2.1 From 787383732d45e6565f0e68e268a7157e3198cd8c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 15:57:32 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.2.0=20=E2=86=92=2025.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 92fd8354..6e543e94 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.2.0 +current_version = 25.3.0 commit = True tag = True diff --git a/setup.py b/setup.py index 5fa237d5..4b3bf3fe 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.2.0", + version="25.3.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 7c32dac163b1d8f1256caf0a4e42ed19ff74d150 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 15:57:32 -0400 Subject: Added tag v25.3.0 for changeset 2371456ae99d --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 75a9fb08..ccc770e2 100644 --- a/.hgtags +++ b/.hgtags @@ -301,3 +301,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 76143bb477b50314ab6f4ccc4ced80ee43f0dc94 v25.1.5 2db4c66aeae47217aaf92099a9875e9e810c9cbb v25.1.6 2083d7c3fadcf15b3bc07f7532440efbcf8fd18d v25.2.0 +2371456ae99d11187c33deacf1308aded31081d9 v25.3.0 -- cgit v1.2.1 From fc6050ad4c1481be0a1aba1f056e76aa8be50039 Mon Sep 17 00:00:00 2001 From: Daniel Holth Date: Fri, 19 Aug 2016 15:58:00 -0400 Subject: changelog --- CHANGES.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5a2b9928..3c4b118e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,14 @@ v25.1.3 * #714 and #704: Revert fix as it breaks other components downstream that can't handle unicode. See #709, #710, and #712. +* Add Extension(py_limited_api=True). When set to a truthy value, + that extension gets a filename apropriate for code using Py_LIMITED_API. + When used correctly this allows a single compiled extension to work on + all future versions of CPython 3. + The py_limited_api argument only controls the filename. To be + compatible with multiple versions of Python 3, the C extension + will also need to set -DPy_LIMITED_API=... and be modified to use + only the functions in the limited API. v25.1.2 ------- -- cgit v1.2.1 From c6d604f053b12920d774324b9fa36211fa9e2eaf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:01:42 -0400 Subject: Move changelog into a new release --- CHANGES.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 10be5821..cd203952 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,18 @@ CHANGES ======= +v25.4.0 +------- + +* Add Extension(py_limited_api=True). When set to a truthy value, + that extension gets a filename apropriate for code using Py_LIMITED_API. + When used correctly this allows a single compiled extension to work on + all future versions of CPython 3. + The py_limited_api argument only controls the filename. To be + compatible with multiple versions of Python 3, the C extension + will also need to set -DPy_LIMITED_API=... and be modified to use + only the functions in the limited API. + v25.3.0 ------- @@ -44,14 +56,6 @@ v25.1.3 * #714 and #704: Revert fix as it breaks other components downstream that can't handle unicode. See #709, #710, and #712. -* Add Extension(py_limited_api=True). When set to a truthy value, - that extension gets a filename apropriate for code using Py_LIMITED_API. - When used correctly this allows a single compiled extension to work on - all future versions of CPython 3. - The py_limited_api argument only controls the filename. To be - compatible with multiple versions of Python 3, the C extension - will also need to set -DPy_LIMITED_API=... and be modified to use - only the functions in the limited API. v25.1.2 ------- -- cgit v1.2.1 From c7ee084a4e94b2061f50b7a6633f814cf3caf615 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:02:23 -0400 Subject: Reorganize imports --- setuptools/command/build_ext.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 7bb4d24c..81d0c32c 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -1,12 +1,12 @@ +import os +import sys +import itertools from distutils.command.build_ext import build_ext as _du_build_ext from distutils.file_util import copy_file from distutils.ccompiler import new_compiler from distutils.sysconfig import customize_compiler from distutils.errors import DistutilsError from distutils import log -import os -import sys -import itertools from setuptools.extension import Library @@ -104,7 +104,7 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] - if (sys.version_info[0] != 2 + if (sys.version_info[0] != 2 and getattr(ext, 'py_limited_api') and get_abi3_suffix()): from distutils.sysconfig import get_config_var -- cgit v1.2.1 From a3a04186f0b0b8fb9206f2813a768d655a7acc04 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:02:39 -0400 Subject: Move import to top --- setuptools/command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 81d0c32c..df8f03fc 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -1,6 +1,7 @@ import os import sys import itertools +import imp from distutils.command.build_ext import build_ext as _du_build_ext from distutils.file_util import copy_file from distutils.ccompiler import new_compiler @@ -61,7 +62,6 @@ if_dl = lambda s: s if have_rtld else '' def get_abi3_suffix(): """Return the file extension for an abi3-compliant Extension()""" - import imp for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION): if '.abi3' in suffix: # Unix return suffix -- cgit v1.2.1 From e3b053192c96cc247c3e12dae78923631397dce7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:05:04 -0400 Subject: Move import to the top --- setuptools/command/build_ext.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index df8f03fc..ca46b22a 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -5,7 +5,7 @@ import imp from distutils.command.build_ext import build_ext as _du_build_ext from distutils.file_util import copy_file from distutils.ccompiler import new_compiler -from distutils.sysconfig import customize_compiler +from distutils.sysconfig import customize_compiler, get_config_var from distutils.errors import DistutilsError from distutils import log @@ -17,10 +17,8 @@ try: except ImportError: _build_ext = _du_build_ext -from distutils.sysconfig import get_config_var - -get_config_var("LDSHARED") # make sure _config_vars is initialized -del get_config_var +# make sure _config_vars is initialized +get_config_var("LDSHARED") from distutils.sysconfig import _config_vars as _CONFIG_VARS @@ -107,7 +105,6 @@ class build_ext(_build_ext): if (sys.version_info[0] != 2 and getattr(ext, 'py_limited_api') and get_abi3_suffix()): - from distutils.sysconfig import get_config_var so_ext = get_config_var('SO') filename = filename[:-len(so_ext)] filename = filename + get_abi3_suffix() -- cgit v1.2.1 From 097e92abaa7b72815a5eb9c232293ef4bbc1b626 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:06:58 -0400 Subject: Extract variable for boolean expression for nicer indentation. --- setuptools/command/build_ext.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index ca46b22a..9caabfd5 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -10,6 +10,7 @@ from distutils.errors import DistutilsError from distutils import log from setuptools.extension import Library +from setuptools.extern import six try: # Attempt to use Cython for building extensions, if available @@ -102,9 +103,12 @@ class build_ext(_build_ext): filename = _build_ext.get_ext_filename(self, fullname) if fullname in self.ext_map: ext = self.ext_map[fullname] - if (sys.version_info[0] != 2 + use_abi3 = ( + six.PY3 and getattr(ext, 'py_limited_api') - and get_abi3_suffix()): + and get_abi3_suffix() + ) + if use_abi3: so_ext = get_config_var('SO') filename = filename[:-len(so_ext)] filename = filename + get_abi3_suffix() -- cgit v1.2.1 From 2cde914c7bc8c8e5a59f937317d47898b38426e4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:09:06 -0400 Subject: Use six for python major version detection --- setuptools/tests/test_build_ext.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index f2e1f59d..0edc92ec 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -1,7 +1,9 @@ import sys import distutils.command.build_ext as orig - from distutils.sysconfig import get_config_var + +from setuptools.extern import six + from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution from setuptools.extension import Extension @@ -27,7 +29,7 @@ class TestBuildExt: of Python 3 if 'is_abi3' is truthy on Extension() """ print(get_abi3_suffix()) - + extension = Extension('spam.eggs', ['eggs.c'], py_limited_api=True) dist = Distribution(dict(ext_modules=[extension])) cmd = build_ext(dist) @@ -35,9 +37,9 @@ class TestBuildExt: assert 'spam.eggs' in cmd.ext_map res = cmd.get_ext_filename('spam.eggs') - if sys.version_info[0] == 2 or not get_abi3_suffix(): + if six.PY2 or not get_abi3_suffix(): assert res.endswith(get_config_var('SO')) elif sys.platform == 'win32': assert res.endswith('eggs.pyd') else: - assert 'abi3' in res \ No newline at end of file + assert 'abi3' in res -- cgit v1.2.1 From cea7420c9417f14f6c069dabf1df23055f681af1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:10:05 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.3.0=20=E2=86=92=2025.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 6e543e94..b089c68d 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.3.0 +current_version = 25.4.0 commit = True tag = True diff --git a/setup.py b/setup.py index 4b3bf3fe..cd4353d0 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.3.0", + version="25.4.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 8aae14c12ea79b6d293e40db02c027e1279a46a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:10:05 -0400 Subject: Added tag v25.4.0 for changeset f713f9faaaa3 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index ccc770e2..06083e1c 100644 --- a/.hgtags +++ b/.hgtags @@ -302,3 +302,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 2db4c66aeae47217aaf92099a9875e9e810c9cbb v25.1.6 2083d7c3fadcf15b3bc07f7532440efbcf8fd18d v25.2.0 2371456ae99d11187c33deacf1308aded31081d9 v25.3.0 +f713f9faaaa33c0e9a628dc9322ef8d1fbeb8319 v25.4.0 -- cgit v1.2.1 From a7931d03be180878538bae00c78d93304e5ca09b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:26:18 -0400 Subject: Rewrite test, dynamically generating the .tar.gz file, with help from dstufft in #748. --- setuptools/tests/test_archive_util.py | 50 +++++++++++++---------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index 3e679c11..b789e9ac 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -1,5 +1,8 @@ # coding: utf-8 +import tarfile +import io + from setuptools.extern import six import pytest @@ -12,41 +15,24 @@ def tarfile_with_unicode(tmpdir): """ Create a tarfile containing only a file whose name is a zero byte file called testimäge.png. - - TODO: Is it possible to generate this file programmatically? """ - data = ( - b'\x1f\x8b\x08\x00R\xfe\x9fW\x00\x03\xed\xd6AO\x13A\x14\x00' - b'\xe0\x11b\x8c\x8d\' z\xf02\x07=x\xd9\xce\xcc\xce\xec\xd4\xc3' - b'&\xa21\xb4X\\lI\xa3r0\xc3v\xc1\x8a\xec6\xcbbz3\x1cH\x80x\x93' - b'x\xeaQ\x12\x13\x0f$z\xd5\x13\x17\xa9U\x8e&\xfc\x06\x13\xff' - b'\x82\xb3\xa5\n\xb6J!\x16\x8c\xf0\xbed2\x9d\xd7\xe9v\xba\xb3' - b'\xafo\x8c\xe4\xa8\xaa\xa4=U\xf4\xc2\xa4\xf1 \xf2f\xa3\xd2\xcc' - b'\xfa\xcb)\xcf(\xfbS\xa8K\x08!\x16\xe78\xee\xa5%\x1a=a\xdb' - b'\xe3\x06FLL\x99\xe4R#\x9cbB%\xa5\x1c\xe1J\xb7\x16\xb0\x97' - b'\xb9\xd9H\x85z)\x8fT\xa8\xdc\xe0\xcf\xf3\xf4\xb4\xc9\xc9=\xae' - b'\xb3\xfdS\xf0\xcf\xfe?\xc1$.\xab\xe8\xa1m\xb4\xed~\x82\x11' - b'\xec\xea\xb1gS.\t%&e,\x8e\xa9\xdd1"S\tf\xe2\xfc\x8dt&{\xcf(zO' - b'lj\xe9]d\x8c\xec\n\x97\xfc\xc0\xe6\xdc\x12\x84\x9a2AS?\xc2\xfe' - b'\xe3\x92?m\xd3\xc4\xbf\xbe\x05\'Z\xfb\xbew\xff;:\xe5\xbf)dK\xfe' - b'\x0b*\x04\xc2\xa4\xfbKiw\xc2\xf3\x1f\x9d>\x7f\x06\xf5 4\xa2\\' - b'\xec\xe4\xf1]\xdc\x14\xc7\xd0Y\xdd\x98n\xefu\x8b\xc7\xdf\xf6w' - b'\xc9\xc1\xb1\xb1\\\xf3e\xfc\x89\xaan\xf9\x96)\xa7v\xe2\x17\xdc' - b'`\xc6(\x86Ay"\xa8\x18*\x8a\xc2\xd2\xc4\x9c~"\xf5\x9b\x95\xea' - b'\xeb\xc2\xf0\x95Z2][[t>\xd63\x9f\xb2K\x9b\x1b\xce\xed\xfa\x9d7' - b'\xb9W\x85\xdaH\xf6\xf3\x87z\xef\xd2\xe5\x17+\x03\xf3\x0b\xb9' - b'\xbe[}\xf3\xe7V\xab+h\xa8w\xbd\xecl\x86\xfd\xce\xf3\x9e/\xd7' - b'\x9e\xbe}\xb6|ih||uk\xeb>z\xb7v\xf1\xeb\xdf\xdf\xafcf\xa7\xfa' - b'\x1f\xde\xbf@\xc7\xfa\xcfEK\xfe[B\x10\xa8\xffGAW\xe9F\xfd\xefP' - b'\xfb\x894\x7f[\xfb\xcd\x14\xcef\xae\x0f\xe6tE/\xdc4\xdc\xd0' - b'\xd33\x02\xbf9\xcbJq}f\xe0t\xdf\'\x04~\x95\xeb0\x9c\x10\x8e' - b'\xd0a\xd7\xfeX\xa7\xfc\x8f\xf3\xe5\xd7\xfc\xe7\xc2\xe2P\xff' - b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x008\xa8\xef\xb1g\xe1Z\x00(\x00\x00' - ) + tarobj = io.BytesIO() + + with tarfile.open(fileobj=tarobj, mode="w:gz") as tgz: + data = b"" + + filename = "testimäge.png" + if six.PY2: + filename = filename.decode('utf-8') + + t = tarfile.TarInfo(filename) + t.size = len(data) + + tgz.addfile(t, io.BytesIO(data)) + target = tmpdir / 'unicode-pkg-1.0.tar.gz' with open(str(target), mode='wb') as tf: - tf.write(data) + tf.write(tarobj.getvalue()) return str(target) -- cgit v1.2.1 From c296634d44de4a9715d09e4773b73b2d233fbbc4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Aug 2016 16:50:58 -0400 Subject: Pin to pytest < 3 until skip errors can be resolved. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cd4353d0..db619735 100755 --- a/setup.py +++ b/setup.py @@ -181,7 +181,7 @@ setup_params = dict( tests_require=[ 'setuptools[ssl]', 'pytest-flake8', - 'pytest>=2.8', + 'pytest>=2.8,<3.0dev', ] + (['mock'] if sys.version_info[:2] < (3, 3) else []), setup_requires=[ ] + pytest_runner + wheel, -- cgit v1.2.1 From 08511f5fdfe04a8ea6e3ae73e086e8a29a0a5cd1 Mon Sep 17 00:00:00 2001 From: stepshal Date: Sat, 20 Aug 2016 04:37:32 +0700 Subject: Fix quantity of blank lines after code object, class of function definition. --- pkg_resources/__init__.py | 55 +++++++++++++++++++++++++++++++ pkg_resources/extern/__init__.py | 2 ++ pkg_resources/tests/test_markers.py | 1 + pkg_resources/tests/test_pkg_resources.py | 8 ++++- pkg_resources/tests/test_resources.py | 4 +++ setuptools/command/build_ext.py | 2 ++ setuptools/tests/test_build_ext.py | 1 + setuptools/tests/test_manifest.py | 1 + 8 files changed, 73 insertions(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 87455a0d..033fc8aa 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -209,10 +209,12 @@ def parse_version(v): _state_vars = {} + def _declare_state(vartype, **kw): globals().update(kw) _state_vars.update(dict.fromkeys(kw, vartype)) + def __getstate__(): state = {} g = globals() @@ -220,25 +222,31 @@ def __getstate__(): state[k] = g['_sget_' + v](g[k]) return state + def __setstate__(state): g = globals() for k, v in state.items(): g['_sset_' + _state_vars[k]](k, g[k], v) return state + def _sget_dict(val): return val.copy() + def _sset_dict(key, ob, state): ob.clear() ob.update(state) + def _sget_object(val): return val.__getstate__() + def _sset_object(key, ob, state): ob.__setstate__(state) + _sget_none = _sset_none = lambda *args: None @@ -265,6 +273,7 @@ def get_supported_platform(): pass return plat + __all__ = [ # Basic resource access and distribution/entry point discovery 'require', 'run_script', 'get_provider', 'get_distribution', @@ -311,8 +320,10 @@ __all__ = [ 'run_main', 'AvailableDistributions', ] + class ResolutionError(Exception): """Abstract base for dependency resolution errors""" + def __repr__(self): return self.__class__.__name__ + repr(self.args) @@ -391,6 +402,8 @@ class DistributionNotFound(ResolutionError): class UnknownExtra(ResolutionError): """Distribution doesn't have an "extra feature" of the given name""" + + _provider_factories = {} PY_MAJOR = sys.version[:3] @@ -400,6 +413,7 @@ SOURCE_DIST = 1 CHECKOUT_DIST = 0 DEVELOP_DIST = -1 + def register_loader_type(loader_type, provider_factory): """Register `provider_factory` to make providers for `loader_type` @@ -409,6 +423,7 @@ def register_loader_type(loader_type, provider_factory): """ _provider_factories[loader_type] = provider_factory + def get_provider(moduleOrReq): """Return an IResourceProvider for the named module or requirement""" if isinstance(moduleOrReq, Requirement): @@ -421,6 +436,7 @@ def get_provider(moduleOrReq): loader = getattr(module, '__loader__', None) return _find_adapter(_provider_factories, loader)(module) + def _macosx_vers(_cache=[]): if not _cache: version = platform.mac_ver()[0] @@ -436,9 +452,11 @@ def _macosx_vers(_cache=[]): _cache.append(version.split('.')) return _cache[0] + def _macosx_arch(machine): return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + def get_build_platform(): """Return this platform's string for platform-specific distributions @@ -464,6 +482,7 @@ def get_build_platform(): pass return plat + macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") # XXX backward compat @@ -524,9 +543,11 @@ def run_script(dist_spec, script_name): ns['__name__'] = name require(dist_spec)[0].run_script(script_name, ns) + # backward compatibility run_main = run_script + def get_distribution(dist): """Return a current distribution object for a Requirement or string""" if isinstance(dist, six.string_types): @@ -537,14 +558,17 @@ def get_distribution(dist): raise TypeError("Expected string, Requirement, or Distribution", dist) return dist + def load_entry_point(dist, group, name): """Return `name` entry point of `group` for `dist` or raise ImportError""" return get_distribution(dist).load_entry_point(group, name) + def get_entry_map(dist, group=None): """Return the entry point map for `group`, or the full entry map""" return get_distribution(dist).get_entry_map(group) + def get_entry_info(dist, group, name): """Return the EntryPoint object for `group`+`name`, or ``None``""" return get_distribution(dist).get_entry_info(group, name) @@ -1332,6 +1356,7 @@ class ResourceManager: """ # XXX + def get_default_cache(): """Determine the default cache location @@ -1376,6 +1401,7 @@ def get_default_cache(): "Please set the PYTHON_EGG_CACHE environment variable" ) + def safe_name(name): """Convert an arbitrary string to a standard distribution name @@ -1538,6 +1564,7 @@ class NullProvider: "Can't perform this operation for loaders without 'get_data()'" ) + register_loader_type(object, NullProvider) @@ -1562,6 +1589,7 @@ class EggProvider(NullProvider): old = path path, base = os.path.split(path) + class DefaultProvider(EggProvider): """Provides access to package resources in the filesystem""" @@ -1587,6 +1615,7 @@ class DefaultProvider(EggProvider): type(None)) register_loader_type(loader_cls, cls) + DefaultProvider._register() @@ -1601,6 +1630,7 @@ class EmptyProvider(NullProvider): def __init__(self): pass + empty_provider = EmptyProvider() @@ -1836,6 +1866,7 @@ class ZipProvider(EggProvider): def _resource_to_zip(self, resource_name): return self._zipinfo_name(self._fn(self.module_path, resource_name)) + register_loader_type(zipimport.zipimporter, ZipProvider) @@ -1913,8 +1944,10 @@ class EggMetadata(ZipProvider): self.module_path = importer.archive self._setup_prefix() + _declare_state('dict', _distribution_finders={}) + def register_finder(importer_type, distribution_finder): """Register `distribution_finder` to find distributions in sys.path items @@ -1931,6 +1964,7 @@ def find_distributions(path_item, only=False): finder = _find_adapter(_distribution_finders, importer) return finder(importer, path_item, only) + def find_eggs_in_zip(importer, path_item, only=False): """ Find eggs in zip files; possibly multiple nested eggs. @@ -1951,12 +1985,17 @@ def find_eggs_in_zip(importer, path_item, only=False): for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath): yield dist + register_finder(zipimport.zipimporter, find_eggs_in_zip) + def find_nothing(importer, path_item, only=False): return () + + register_finder(object, find_nothing) + def find_on_path(importer, path_item, only=False): """Yield distributions accessible on a sys.path directory""" path_item = _normalize_cached(path_item) @@ -1997,6 +2036,8 @@ def find_on_path(importer, path_item, only=False): for item in dists: yield item break + + register_finder(pkgutil.ImpImporter, find_on_path) if hasattr(importlib_machinery, 'FileFinder'): @@ -2023,6 +2064,7 @@ def register_namespace_handler(importer_type, namespace_handler): """ _namespace_handlers[importer_type] = namespace_handler + def _handle_ns(packageName, path_item): """Ensure that named package includes a subpath of path_item (if needed)""" @@ -2055,6 +2097,7 @@ def _rebuild_mod_path(orig_path, package_name, module): corresponding to their sys.path order """ sys_path = [_normalize_cached(p) for p in sys.path] + def position_in_sys_path(path): """ Return the ordinal of the path based on its position in sys.path @@ -2100,6 +2143,7 @@ def declare_namespace(packageName): finally: _imp.release_lock() + def fixup_namespace_packages(path_item, parent=None): """Ensure that previously-declared namespace packages include path_item""" _imp.acquire_lock() @@ -2111,6 +2155,7 @@ def fixup_namespace_packages(path_item, parent=None): finally: _imp.release_lock() + def file_ns_handler(importer, path_item, packageName, module): """Compute an ns-package subpath for a filesystem or zipfile importer""" @@ -2123,6 +2168,7 @@ def file_ns_handler(importer, path_item, packageName, module): # Only return the path if it's not already there return subpath + register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) register_namespace_handler(zipimport.zipimporter, file_ns_handler) @@ -2133,6 +2179,7 @@ if hasattr(importlib_machinery, 'FileFinder'): def null_ns_handler(importer, path_item, packageName, module): return None + register_namespace_handler(object, null_ns_handler) @@ -2140,6 +2187,7 @@ def normalize_path(filename): """Normalize a file/dir name for comparison purposes""" return os.path.normcase(os.path.realpath(filename)) + def _normalize_cached(filename, _cache={}): try: return _cache[filename] @@ -2147,6 +2195,7 @@ def _normalize_cached(filename, _cache={}): _cache[filename] = result = normalize_path(filename) return result + def _is_unpacked_egg(path): """ Determine if given path appears to be an unpacked egg. @@ -2155,6 +2204,7 @@ def _is_unpacked_egg(path): path.lower().endswith('.egg') ) + def _set_parent_ns(packageName): parts = packageName.split('.') name = parts.pop() @@ -2176,6 +2226,7 @@ def yield_lines(strs): for s in yield_lines(ss): yield s + MODULE = re.compile(r"\w+(\.\w+)*$").match EGG_NAME = re.compile( r""" @@ -2783,6 +2834,7 @@ def issue_warning(*args, **kw): class RequirementParseError(ValueError): + def __str__(self): return ' '.join(self.args) @@ -2807,6 +2859,7 @@ def parse_requirements(strs): class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" try: @@ -2867,6 +2920,7 @@ def _get_mro(cls): return cls.__mro__[1:] return cls.__mro__ + def _find_adapter(registry, ob): """Return an adapter factory for `ob` from `registry`""" for t in _get_mro(getattr(ob, '__class__', type(ob))): @@ -2916,6 +2970,7 @@ def split_sections(s): # wrap up last segment yield section, content + def _mkstemp(*args, **kw): old_open = os.open try: diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index 6758d36f..492f66f1 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -6,6 +6,7 @@ class VendorImporter: A PEP 302 meta path importer for finding optionally-vendored or otherwise naturally-installed packages from root_name. """ + def __init__(self, root_name, vendored_names=(), vendor_pkg=None): self.root_name = root_name self.vendored_names = set(vendored_names) @@ -67,5 +68,6 @@ class VendorImporter: if self not in sys.meta_path: sys.meta_path.append(self) + names = 'packaging', 'pyparsing', 'six' VendorImporter(__name__, names).install() diff --git a/pkg_resources/tests/test_markers.py b/pkg_resources/tests/test_markers.py index 8d451de3..78810b6e 100644 --- a/pkg_resources/tests/test_markers.py +++ b/pkg_resources/tests/test_markers.py @@ -5,6 +5,7 @@ except ImportError: from pkg_resources import evaluate_marker + @mock.patch('platform.python_version', return_value='2.7.10') def test_ordering(python_version_mock): assert evaluate_marker("python_full_version > '2.7.3'") is True diff --git a/pkg_resources/tests/test_pkg_resources.py b/pkg_resources/tests/test_pkg_resources.py index 8b276ffc..361fe657 100644 --- a/pkg_resources/tests/test_pkg_resources.py +++ b/pkg_resources/tests/test_pkg_resources.py @@ -24,6 +24,7 @@ try: except NameError: unicode = str + def timestamp(dt): """ Return a timestamp for a local, naive datetime instance. @@ -34,13 +35,16 @@ def timestamp(dt): # Python 3.2 and earlier return time.mktime(dt.timetuple()) + class EggRemover(unicode): + def __call__(self): if self in sys.path: sys.path.remove(self) if os.path.exists(self): os.remove(self) + class TestZipProvider(object): finalizers = [] @@ -94,7 +98,9 @@ class TestZipProvider(object): assert f.read() == 'hello, world!' manager.cleanup_resources() + class TestResourceManager(object): + def test_get_cache_path(self): mgr = pkg_resources.ResourceManager() path = mgr.get_cache_path('foo') @@ -107,6 +113,7 @@ class TestIndependence: """ Tests to ensure that pkg_resources runs independently from setuptools. """ + def test_setuptools_not_imported(self): """ In a separate Python environment, import pkg_resources and assert @@ -122,7 +129,6 @@ class TestIndependence: subprocess.check_call(cmd) - class TestDeepVersionLookupDistutils(object): @pytest.fixture diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py index 1d663b83..2ed56233 100644 --- a/pkg_resources/tests/test_resources.py +++ b/pkg_resources/tests/test_resources.py @@ -34,6 +34,7 @@ class Metadata(pkg_resources.EmptyProvider): dist_from_fn = pkg_resources.Distribution.from_filename + class TestDistro: def testCollection(self): @@ -294,6 +295,7 @@ class TestDistro: class TestWorkingSet: + def test_find_conflicting(self): ws = WorkingSet([]) Foo = Distribution.from_filename("/foo_dir/Foo-1.2.egg") @@ -380,6 +382,7 @@ class TestEntryPoints: assert ep.name == 'html+mako' reject_specs = "foo", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2" + @pytest.mark.parametrize("reject_spec", reject_specs) def test_reject_spec(self, reject_spec): with pytest.raises(ValueError): @@ -434,6 +437,7 @@ class TestEntryPoints: with pytest.raises(ValueError): EntryPoint.parse_map(self.submap_str) + class TestRequirements: def testBasics(self): diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 9caabfd5..f994b626 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -59,6 +59,7 @@ elif os.name != 'nt': if_dl = lambda s: s if have_rtld else '' + def get_abi3_suffix(): """Return the file extension for an abi3-compliant Extension()""" for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION): @@ -67,6 +68,7 @@ def get_abi3_suffix(): elif suffix == '.pyd': # Windows return suffix + class build_ext(_build_ext): def run(self): diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 0edc92ec..ac002f44 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -8,6 +8,7 @@ from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution from setuptools.extension import Extension + class TestBuildExt: def test_get_ext_filename(self): diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 6e67ca61..6360270d 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -51,6 +51,7 @@ def quiet(): def touch(filename): open(filename, 'w').close() + # The set of files always in the manifest, including all files in the # .egg-info directory default_files = frozenset(map(make_local_path, [ -- cgit v1.2.1 From e3ffde678c36ca778476574194cdc6d436d82263 Mon Sep 17 00:00:00 2001 From: stepshal Date: Sat, 20 Aug 2016 08:15:52 +0700 Subject: Remove trailing whitespace. (#751) --- setuptools/msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index bffaa6aa..26e399cc 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -223,7 +223,7 @@ def msvc14_get_vc_env(plat_spec): def msvc14_gen_lib_options(*args, **kwargs): """ - Patched "distutils._msvccompiler.gen_lib_options" for fix + Patched "distutils._msvccompiler.gen_lib_options" for fix compatibility between "numpy.distutils" and "distutils._msvccompiler" (for Numpy < 1.11.2) """ -- cgit v1.2.1 From d687531884ee25d5a517d529b2fd535831b70115 Mon Sep 17 00:00:00 2001 From: Torsten Landschoff Date: Sat, 20 Aug 2016 09:23:24 +0200 Subject: Make Extension accept positional arguments again (fixes #752). As a side effect, py_limited_api may now only be passed as keyword argument. I think it is early enough for this feature to change this and enforce the flag to be passed as a keyword... --- setuptools/extension.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setuptools/extension.py b/setuptools/extension.py index da94c3fa..f8058b72 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -36,9 +36,11 @@ have_pyrex = _have_cython class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" - def __init__(self, name, sources, py_limited_api=False, **kw): - self.py_limited_api = py_limited_api - _Extension.__init__(self, name, sources, **kw) + def __init__(self, name, sources, *args, **kw): + # The *args is needed for compatibility as calls may use positional + # arguments. py_limited_api may be set only via keyword. + self.py_limited_api = kw.pop("py_limited_api", False) + _Extension.__init__(self, name, sources, *args, **kw) def _convert_pyx_sources_to_lang(self): """ -- cgit v1.2.1 From 06df852e7cda567b6f8ab6831486285f0e2989a4 Mon Sep 17 00:00:00 2001 From: Gabi Davar Date: Sat, 20 Aug 2016 15:10:57 +0300 Subject: fix tests on windows (#754) - have tox pass env variables used by setuptools on windows. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 8c09e7df..7ce7ce41 100644 --- a/tox.ini +++ b/tox.ini @@ -2,4 +2,5 @@ envlist = py26,py27,py33,py34,py35,pypy,pypy3 [testenv] +passenv=APPDATA USERPROFILE HOMEDRIVE HOMEPATH windir commands=python setup.py test -- cgit v1.2.1 From 735a66a5588aadcf02a10b58338d476803916b99 Mon Sep 17 00:00:00 2001 From: Ofekmeister Date: Sat, 20 Aug 2016 13:37:33 -0400 Subject: Make import unconditional Put import on top and updated CHANGES.rst --- CHANGES.rst | 3 +++ setuptools/command/easy_install.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cd203952..cc027f41 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ CHANGES v25.4.0 ------- +* #459 via #736: On Windows systems, sys.argv[0] now correctly becomes the + name of entry point. + * Add Extension(py_limited_api=True). When set to a truthy value, that extension gets a filename apropriate for code using Py_LIMITED_API. When used correctly this allows a single compiled extension to work on diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index e2a7dc46..5065661f 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2018,11 +2018,11 @@ class ScriptWriter(object): template = textwrap.dedent(""" # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r __requires__ = %(spec)r + import re import sys from pkg_resources import load_entry_point if __name__ == '__main__': - import re sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit( load_entry_point(%(spec)r, %(group)r, %(name)r)() diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index a4b1dfd5..82e1d7e8 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -74,11 +74,11 @@ class TestEasyInstallTest: expected = header + DALS(""" # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' __requires__ = 'spec' + import re import sys from pkg_resources import load_entry_point if __name__ == '__main__': - import re sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) sys.exit( load_entry_point('spec', 'console_scripts', 'name')() -- cgit v1.2.1 From b9baa94e18e91671f9acde4e0e033be1391a7fde Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 17:44:54 -0400 Subject: Default to gztar for sdists on all platforms. Ref #748. --- CHANGES.rst | 7 +++++++ setuptools/command/sdist.py | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index cd203952..c6839a0d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v26.0.0 +------- + +* #748: By default, sdists are now produced in gzipped tarfile + format by default on all platforms, adding forward compatibility + for the same behavior in Python 3.6 (See Python #27819). + v25.4.0 ------- diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index b6125f58..1d4f5d54 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -66,6 +66,17 @@ class sdist(orig.sdist): if data not in dist_files: dist_files.append(data) + def initialize_options(self): + orig.sdist.initialize_options(self) + + self._default_to_gztar() + + def _default_to_gztar(self): + # only needed on Python prior to 3.6. + if sys.version_info >= (3, 6, 0, 'beta', 1): + return + self.formats = ['gztar'] + def make_distribution(self): """ Workaround for #516 -- cgit v1.2.1 From 0a603394831bbd9d837994aea5fa0342cc7b0291 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 17:50:41 -0400 Subject: Update changelog --- CHANGES.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 065c77ba..e23c4e94 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,12 +9,14 @@ v26.0.0 format by default on all platforms, adding forward compatibility for the same behavior in Python 3.6 (See Python #27819). +* #459 via #736: On Windows with script launchers, + sys.argv[0] now reflects + the name of the entry point, consistent with the behavior in + distlib and pip wrappers. + v25.4.0 ------- -* #459 via #736: On Windows systems, sys.argv[0] now correctly becomes the - name of entry point. - * Add Extension(py_limited_api=True). When set to a truthy value, that extension gets a filename apropriate for code using Py_LIMITED_API. When used correctly this allows a single compiled extension to work on -- cgit v1.2.1 From 6883ff3b2832a0197d52d3d8f0640ae41f52b312 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 18:27:10 -0400 Subject: Update changelog --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e23c4e94..5e5bd614 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,9 @@ v26.0.0 the name of the entry point, consistent with the behavior in distlib and pip wrappers. +* #752 via #753: When indicating ``py_limited_api`` to Extension, + it must be passed as a keyword argument. + v25.4.0 ------- -- cgit v1.2.1 From 22501ae355cbefcea9a58fe7c4ac4395250c1aca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 18:27:14 -0400 Subject: =?UTF-8?q?Bump=20version:=2025.4.0=20=E2=86=92=2026.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index b089c68d..cf5d01a8 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 25.4.0 +current_version = 26.0.0 commit = True tag = True diff --git a/setup.py b/setup.py index db619735..16d752fe 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="25.4.0", + version="26.0.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From b51be80bd1496a2c5338f41dc7c663409c1b53a0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 18:27:14 -0400 Subject: Added tag v26.0.0 for changeset 7cb13a0cd176 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 06083e1c..6e59e318 100644 --- a/.hgtags +++ b/.hgtags @@ -303,3 +303,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 2083d7c3fadcf15b3bc07f7532440efbcf8fd18d v25.2.0 2371456ae99d11187c33deacf1308aded31081d9 v25.3.0 f713f9faaaa33c0e9a628dc9322ef8d1fbeb8319 v25.4.0 +7cb13a0cd176f39500701b24dbfec603ead5110c v26.0.0 -- cgit v1.2.1 From 64727bb4d1eb2a44d2b9a67be8d7c613fe78ccef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 20:21:01 -0400 Subject: Issue is fixed, so just exclude the offending version. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 16d752fe..f3084a4e 100755 --- a/setup.py +++ b/setup.py @@ -181,7 +181,7 @@ setup_params = dict( tests_require=[ 'setuptools[ssl]', 'pytest-flake8', - 'pytest>=2.8,<3.0dev', + 'pytest>=2.8,!=3.0.0', ] + (['mock'] if sys.version_info[:2] < (3, 3) else []), setup_requires=[ ] + pytest_runner + wheel, -- cgit v1.2.1 From 1aa71905fc9bd9a13f2b0c371e869ae8eb27308c Mon Sep 17 00:00:00 2001 From: stepshal Date: Mon, 22 Aug 2016 19:25:13 +0700 Subject: Make exactly one space after comma. (#756) --- pkg_resources/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 033fc8aa..27d70a60 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -276,7 +276,7 @@ def get_supported_platform(): __all__ = [ # Basic resource access and distribution/entry point discovery - 'require', 'run_script', 'get_provider', 'get_distribution', + 'require', 'run_script', 'get_provider', 'get_distribution', 'load_entry_point', 'get_entry_map', 'get_entry_info', 'iter_entry_points', 'resource_string', 'resource_stream', 'resource_filename', -- cgit v1.2.1 From 691e6ac03339d6aef045e05872b286d91e9f49b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 27 Aug 2016 08:45:33 -0400 Subject: Add appdirs as vendored package. Ref #763. --- pavement.py | 2 + pkg_resources/_vendor/appdirs.py | 552 ++++++++++++++++++++++++++ pkg_resources/_vendor/packaging/specifiers.py | 2 +- pkg_resources/_vendor/vendored.txt | 1 + pkg_resources/extern/__init__.py | 2 +- 5 files changed, 557 insertions(+), 2 deletions(-) create mode 100644 pkg_resources/_vendor/appdirs.py diff --git a/pavement.py b/pavement.py index 3d840086..f85617d4 100644 --- a/pavement.py +++ b/pavement.py @@ -12,9 +12,11 @@ def remove_all(paths): @task def update_vendored(): vendor = Path('pkg_resources/_vendor') + # pip uninstall doesn't support -t, so do it manually remove_all(vendor.glob('packaging*')) remove_all(vendor.glob('six*')) remove_all(vendor.glob('pyparsing*')) + remove_all(vendor.glob('appdirs*')) install_args = [ 'install', '-r', str(vendor / 'vendored.txt'), diff --git a/pkg_resources/_vendor/appdirs.py b/pkg_resources/_vendor/appdirs.py new file mode 100644 index 00000000..f4dba095 --- /dev/null +++ b/pkg_resources/_vendor/appdirs.py @@ -0,0 +1,552 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 0) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ + Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\Application Data\\ + Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ + Win 7 (not roaming): C:\Users\\AppData\Local\\ + Win 7 (roaming): C:\Users\\AppData\Roaming\\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/', + if XDG_DATA_DIRS is not set + + Typical user data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by deafult "~/.config/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + """Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set + + Typical user data directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: C:\Users\\AppData\Local\\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Logs/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname, appauthor=None, version=None, roaming=False, + multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernal.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", "site_data_dir", + "user_config_dir", "site_config_dir", + "user_cache_dir", "user_log_dir") + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/pkg_resources/_vendor/packaging/specifiers.py b/pkg_resources/_vendor/packaging/specifiers.py index 9b6353f0..7f5a76cf 100644 --- a/pkg_resources/_vendor/packaging/specifiers.py +++ b/pkg_resources/_vendor/packaging/specifiers.py @@ -198,7 +198,7 @@ class _IndividualSpecifier(BaseSpecifier): (prereleases or self.prereleases)): found_prereleases.append(version) # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. + # accepting prereleases from the begining. else: yielded = True yield version diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index a30a409d..2b51c547 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -1,3 +1,4 @@ packaging==16.7 pyparsing==2.1.8 six==1.10.0 +appdirs==1.4.0 diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index 492f66f1..b4156fec 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -69,5 +69,5 @@ class VendorImporter: sys.meta_path.append(self) -names = 'packaging', 'pyparsing', 'six' +names = 'packaging', 'pyparsing', 'six', 'appdirs' VendorImporter(__name__, names).install() -- cgit v1.2.1 From beec7ff9b0e301e6fffb21f9a8d999829eb8e678 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 27 Aug 2016 08:55:25 -0400 Subject: Rely on appdirs for resolving a cache dir for Python-Eggs. Fixes #763. --- CHANGES.rst | 8 ++++++++ pkg_resources/__init__.py | 50 +++++++++-------------------------------------- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 5e5bd614..78cea807 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,14 @@ CHANGES ======= +v26.1.0 +------- + +* #763: ``pkg_resources.get_default_cache`` now defers to the + `appdirs project `_ to + resolve the cache directory. Adds a vendored dependency on + appdirs to pkg_resources. + v26.0.0 ------- diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 27d70a60..a93a3da7 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -65,6 +65,7 @@ try: except ImportError: importlib_machinery = None +from pkg_resources.extern import appdirs from pkg_resources.extern import packaging __import__('pkg_resources.extern.packaging.version') __import__('pkg_resources.extern.packaging.specifiers') @@ -1358,48 +1359,15 @@ class ResourceManager: def get_default_cache(): - """Determine the default cache location - - This returns the ``PYTHON_EGG_CACHE`` environment variable, if set. - Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of the - "Application Data" directory. On all other systems, it's "~/.python-eggs". """ - try: - return os.environ['PYTHON_EGG_CACHE'] - except KeyError: - pass - - if os.name != 'nt': - return os.path.expanduser('~/.python-eggs') - - # XXX this may be locale-specific! - app_data = 'Application Data' - app_homes = [ - # best option, should be locale-safe - (('APPDATA',), None), - (('USERPROFILE',), app_data), - (('HOMEDRIVE', 'HOMEPATH'), app_data), - (('HOMEPATH',), app_data), - (('HOME',), None), - # 95/98/ME - (('WINDIR',), app_data), - ] - - for keys, subdir in app_homes: - dirname = '' - for key in keys: - if key in os.environ: - dirname = os.path.join(dirname, os.environ[key]) - else: - break - else: - if subdir: - dirname = os.path.join(dirname, subdir) - return os.path.join(dirname, 'Python-Eggs') - else: - raise RuntimeError( - "Please set the PYTHON_EGG_CACHE environment variable" - ) + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) def safe_name(name): -- cgit v1.2.1 From 495bb634f8cb450ee11130917de1c1b85486071f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Aug 2016 10:04:07 -0400 Subject: =?UTF-8?q?Bump=20version:=2026.0.0=20=E2=86=92=2026.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index cf5d01a8..eea97c9b 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 26.0.0 +current_version = 26.1.0 commit = True tag = True diff --git a/setup.py b/setup.py index f3084a4e..c37c9980 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="26.0.0", + version="26.1.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From f455ee4f5881fa549c2dc864efc9848e170de6ef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Aug 2016 10:04:08 -0400 Subject: Added tag v26.1.0 for changeset b299cfc9d7d8 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 6e59e318..9977a896 100644 --- a/.hgtags +++ b/.hgtags @@ -304,3 +304,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 2371456ae99d11187c33deacf1308aded31081d9 v25.3.0 f713f9faaaa33c0e9a628dc9322ef8d1fbeb8319 v25.4.0 7cb13a0cd176f39500701b24dbfec603ead5110c v26.0.0 +b299cfc9d7d89070e8eec9751a8be72c8a75506b v26.1.0 -- cgit v1.2.1 From c7c6522000dc6a32f5933dc837b188b612744b3b Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Mon, 29 Aug 2016 16:20:54 +1000 Subject: Install py.test>=2.8,<3 The 3.x series is currently broken. Until py.test works again, 3.x should not be used by anything. --- .travis.yml | 2 +- setup.py | 2 +- tox.ini | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed077d94..81d1f170 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: - LC_ALL=C LC_CTYPE=C script: # avoid VersionConflict when newer version is required - - pip install -U pytest + - pip install -U 'pytest>=2.8,<3' # Output the env, because the travis docs just can't be trusted - env diff --git a/setup.py b/setup.py index c37c9980..3b7ab31b 100755 --- a/setup.py +++ b/setup.py @@ -181,7 +181,7 @@ setup_params = dict( tests_require=[ 'setuptools[ssl]', 'pytest-flake8', - 'pytest>=2.8,!=3.0.0', + 'pytest>=2.8,<3', ] + (['mock'] if sys.version_info[:2] < (3, 3) else []), setup_requires=[ ] + pytest_runner + wheel, diff --git a/tox.ini b/tox.ini index 7ce7ce41..868ba317 100644 --- a/tox.ini +++ b/tox.ini @@ -2,5 +2,8 @@ envlist = py26,py27,py33,py34,py35,pypy,pypy3 [testenv] +deps= + pytest-flake8 + pytest>=2.8,<3 passenv=APPDATA USERPROFILE HOMEDRIVE HOMEPATH windir -commands=python setup.py test +commands=python setup.py test --addopts='-rsx' -- cgit v1.2.1 From a3a7cac18c91f953957cb5fa440f203ed1f2b5fd Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Mon, 29 Aug 2016 10:28:29 +0200 Subject: easy_install: update links to documentation --- setuptools/command/easy_install.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 5065661f..e8b90c70 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -8,7 +8,7 @@ A tool for doing automatic download/extract/build of distutils-based Python packages. For detailed documentation, see the accompanying EasyInstall.txt file, or visit the `EasyInstall home page`__. -__ https://pythonhosted.org/setuptools/easy_install.html +__ https://setuptools.readthedocs.io/en/latest/easy_install.html """ @@ -512,7 +512,7 @@ class easy_install(Command): For information on other options, you may wish to consult the documentation at: - https://pythonhosted.org/setuptools/easy_install.html + https://setuptools.readthedocs.io/en/latest/easy_install.html Please make the appropriate changes for your system and try again. """).lstrip() @@ -1256,7 +1256,8 @@ class easy_install(Command): * You can set up the installation directory to support ".pth" files by using one of the approaches described here: - https://pythonhosted.org/setuptools/easy_install.html#custom-installation-locations + https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations + Please make the appropriate changes for your system and try again.""").lstrip() -- cgit v1.2.1 From 367b540c62c522fde1f8f048cfb75a6af32fc318 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Aug 2016 16:46:55 -0400 Subject: Update changelog --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 78cea807..be52f56f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v26.1.1 +------- + +* Re-release of 26.1.0 with pytest pinned to allow for automated + deployement and thus proper packaging environment variables, + fixing issues with missing executable launchers. + v26.1.0 ------- -- cgit v1.2.1 From e4ba41ec7f54370a030fb09f9f0a334396f38489 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Aug 2016 16:47:02 -0400 Subject: =?UTF-8?q?Bump=20version:=2026.1.0=20=E2=86=92=2026.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index eea97c9b..3bcd66a9 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 26.1.0 +current_version = 26.1.1 commit = True tag = True diff --git a/setup.py b/setup.py index 3b7ab31b..9263c5b7 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="26.1.0", + version="26.1.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 336258f31655704751d1d0d0b9381d5b4e55b659 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Aug 2016 16:47:02 -0400 Subject: Added tag v26.1.1 for changeset e1d057e23b2f --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 9977a896..88f2dc60 100644 --- a/.hgtags +++ b/.hgtags @@ -305,3 +305,4 @@ c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 f713f9faaaa33c0e9a628dc9322ef8d1fbeb8319 v25.4.0 7cb13a0cd176f39500701b24dbfec603ead5110c v26.0.0 b299cfc9d7d89070e8eec9751a8be72c8a75506b v26.1.0 +e1d057e23b2fec5991084744c356a6c7e05b219d v26.1.1 -- cgit v1.2.1 From 4d035167b3b441e86d58e4d865b8b2c2dd5407a7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 1 Sep 2016 21:42:30 -0300 Subject: Use pytest>=3.0.2 The bug that lead to pin it (pytest-dev/pytest#1888) was fixed in 3.0.2 --- .travis.yml | 2 +- setup.py | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 81d1f170..b8bca7cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: - LC_ALL=C LC_CTYPE=C script: # avoid VersionConflict when newer version is required - - pip install -U 'pytest>=2.8,<3' + - pip install -U 'pytest>=3.0.2' # Output the env, because the travis docs just can't be trusted - env diff --git a/setup.py b/setup.py index 9263c5b7..90702c1d 100755 --- a/setup.py +++ b/setup.py @@ -181,7 +181,7 @@ setup_params = dict( tests_require=[ 'setuptools[ssl]', 'pytest-flake8', - 'pytest>=2.8,<3', + 'pytest>=3.0.2', ] + (['mock'] if sys.version_info[:2] < (3, 3) else []), setup_requires=[ ] + pytest_runner + wheel, diff --git a/tox.ini b/tox.ini index 868ba317..806e6ed2 100644 --- a/tox.ini +++ b/tox.ini @@ -4,6 +4,6 @@ envlist = py26,py27,py33,py34,py35,pypy,pypy3 [testenv] deps= pytest-flake8 - pytest>=2.8,<3 + pytest>=3.0.2 passenv=APPDATA USERPROFILE HOMEDRIVE HOMEPATH windir commands=python setup.py test --addopts='-rsx' -- cgit v1.2.1 From 1bab356fbc59272d9e17f03e52930cd4574ffa0a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 22:28:51 -0400 Subject: Only apply findall patch on affected Pythons. --- setuptools/__init__.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index cf0c39f2..42703947 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,6 +1,7 @@ """Extensions to the 'distutils' for large or complex distributions""" import os +import sys import functools import distutils.core import distutils.filelist @@ -17,7 +18,7 @@ from setuptools.depends import Require __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', - 'find_packages' + 'find_packages', ] __version__ = setuptools.version.__version__ @@ -171,5 +172,14 @@ def findall(dir=os.curdir): return list(files) -# fix findall bug in distutils (http://bugs.python.org/issue12885) -distutils.filelist.findall = findall +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 = findall -- cgit v1.2.1 From 7f8144544f23f4fd84d339d846b91d4e3b703dd4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 22:58:56 -0400 Subject: Provide forward compatibility for Warehouse as the default repository for the upload command. --- CHANGES.rst | 22 ++++++++++++++++++++++ setuptools/__init__.py | 17 +++++++++++++++++ setuptools/command/upload_docs.py | 5 +++++ 3 files changed, 44 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index be52f56f..a45575ac 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,28 @@ CHANGES ======= +v27.0.0 +------- + +* Now use Warehouse by default for + ``upload``, patching ``distutils.config.PyPIRCCommand`` to + affect default behavior. + + Any config in .pypirc should be updated to replace + + https://pypi.python.org/pypi/ + + with + + https://upload.pypi.org/legacy/ + + Similarly, any passwords stored in the keyring should be + updated to use this new value for "system". + + The ``upload_docs`` command will continue to use the python.org + site, but the command is now deprecated. Users are urged to use + Read The Docs instead. + v26.1.1 ------- diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 42703947..2ca97103 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -183,3 +183,20 @@ has_issue_12885 = ( if has_issue_12885: # fix findall bug in distutils (http://bugs.python.org/issue12885) distutils.filelist.findall = 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 diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index ccc1c76f..269dc2d5 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -29,6 +29,10 @@ def _encode(s): class upload_docs(upload): + # override the default repository as upload_docs isn't + # supported by Warehouse (and won't be). + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/' + description = 'Upload documentation to PyPI' user_options = [ @@ -53,6 +57,7 @@ class upload_docs(upload): self.target_dir = None def finalize_options(self): + log.warn("Upload_docs command is deprecated. Use RTD instead.") upload.finalize_options(self) if self.upload_dir is None: if self.has_sphinx(): -- cgit v1.2.1 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/__init__.py | 6 +++--- setuptools/dist.py | 19 ++----------------- setuptools/extension.py | 2 +- setuptools/monkey.py | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+), 21 deletions(-) create mode 100644 setuptools/monkey.py diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 2ca97103..4038ee83 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -5,7 +5,6 @@ import sys import functools import distutils.core import distutils.filelist -from distutils.core import Command as _Command from distutils.util import convert_path from fnmatch import fnmatchcase @@ -13,7 +12,8 @@ from setuptools.extern.six.moves import filterfalse, map import setuptools.version from setuptools.extension import Extension -from setuptools.dist import Distribution, Feature, _get_unpatched +from setuptools.dist import Distribution, Feature +from setuptools.monkey import _get_unpatched from setuptools.depends import Require __all__ = [ @@ -122,7 +122,7 @@ find_packages = PackageFinder.find setup = distutils.core.setup -_Command = _get_unpatched(_Command) +_Command = _get_unpatched(distutils.core.Command) class Command(_Command): diff --git a/setuptools/dist.py b/setuptools/dist.py index 820df6d5..380b9436 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -9,7 +9,6 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist -from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape @@ -20,25 +19,11 @@ from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support +from setuptools.monkey import _get_unpatched import pkg_resources -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 - - -_Distribution = _get_unpatched(_Distribution) +_Distribution = _get_unpatched(distutils.core.Distribution) def _patch_distribution_metadata_write_pkg_file(): diff --git a/setuptools/extension.py b/setuptools/extension.py index f8058b72..7ef3fad2 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -7,7 +7,7 @@ import distutils.extension from setuptools.extern.six.moves import map -from .dist import _get_unpatched +from .monkey import _get_unpatched from . import msvc _Extension = _get_unpatched(distutils.core.Extension) 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.1 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/__init__.py | 38 +++----------------------------------- setuptools/monkey.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 4038ee83..0129658a 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,7 +1,6 @@ """Extensions to the 'distutils' for large or complex distributions""" import os -import sys import functools import distutils.core import distutils.filelist @@ -13,8 +12,8 @@ from setuptools.extern.six.moves import filterfalse, map import setuptools.version from setuptools.extension import Extension from setuptools.dist import Distribution, Feature -from setuptools.monkey import _get_unpatched from setuptools.depends import Require +from . import monkey __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', @@ -122,7 +121,7 @@ find_packages = PackageFinder.find setup = distutils.core.setup -_Command = _get_unpatched(distutils.core.Command) +_Command = monkey._get_unpatched(distutils.core.Command) class Command(_Command): @@ -144,10 +143,6 @@ class Command(_Command): return cmd -# we can't patch distutils.cmd, alas -distutils.core.Command = Command - - def _find_all_simple(path): """ Find all files under 'path' @@ -172,31 +167,4 @@ def findall(dir=os.curdir): return list(files) -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 = 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 +monkey.patch_all() 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.1 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/__init__.py | 2 +- setuptools/dist.py | 4 ++-- setuptools/extension.py | 4 ++-- setuptools/monkey.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 0129658a..41b590d7 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -121,7 +121,7 @@ find_packages = PackageFinder.find setup = distutils.core.setup -_Command = monkey._get_unpatched(distutils.core.Command) +_Command = monkey.get_unpatched(distutils.core.Command) class Command(_Command): diff --git a/setuptools/dist.py b/setuptools/dist.py index 380b9436..7a4249f1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -19,11 +19,11 @@ from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support -from setuptools.monkey import _get_unpatched +from setuptools.monkey import get_unpatched import pkg_resources -_Distribution = _get_unpatched(distutils.core.Distribution) +_Distribution = get_unpatched(distutils.core.Distribution) def _patch_distribution_metadata_write_pkg_file(): diff --git a/setuptools/extension.py b/setuptools/extension.py index 7ef3fad2..073d9459 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -7,10 +7,10 @@ import distutils.extension from setuptools.extern.six.moves import map -from .monkey import _get_unpatched +from .monkey import get_unpatched from . import msvc -_Extension = _get_unpatched(distutils.core.Extension) +_Extension = get_unpatched(distutils.core.Extension) msvc.patch_for_specialized_compiler() 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.1 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/dist.py | 129 +++++++++++++++++++----------------------------- setuptools/extension.py | 15 ++---- setuptools/monkey.py | 41 +++++++++++++++ 3 files changed, 95 insertions(+), 90 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 7a4249f1..d321e6c5 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -23,83 +23,58 @@ from setuptools.monkey import get_unpatched import pkg_resources -_Distribution = get_unpatched(distutils.core.Distribution) - - -def _patch_distribution_metadata_write_pkg_file(): - """Patch write_pkg_file to also write Requires-Python/Requires-External""" - - # Based on Python 3.5 version - def write_pkg_file(self, file): - """Write the PKG-INFO format data to a file object. - """ - version = '1.0' - if (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - version = '1.1' - # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires'): - version = '1.2' - - file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name()) - file.write('Version: %s\n' % self.get_version()) - file.write('Summary: %s\n' % self.get_description()) - file.write('Home-page: %s\n' % self.get_url()) - file.write('Author: %s\n' % self.get_contact()) - file.write('Author-email: %s\n' % self.get_contact_email()) - file.write('License: %s\n' % self.get_license()) - if self.download_url: - file.write('Download-URL: %s\n' % self.download_url) - - long_desc = rfc822_escape(self.get_long_description()) - file.write('Description: %s\n' % long_desc) - - keywords = ','.join(self.get_keywords()) - if keywords: - file.write('Keywords: %s\n' % keywords) - - self._write_list(file, 'Platform', self.get_platforms()) - self._write_list(file, 'Classifier', self.get_classifiers()) - - # PEP 314 - self._write_list(file, 'Requires', self.get_requires()) - self._write_list(file, 'Provides', self.get_provides()) - self._write_list(file, 'Obsoletes', self.get_obsoletes()) - - # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires'): - file.write('Requires-Python: %s\n' % self.python_requires) - - distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file - - -_patch_distribution_metadata_write_pkg_file() - - -def _patch_distribution_metadata_write_pkg_info(): +# Based on Python 3.5 version +def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. """ - 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. + version = '1.0' + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + version = '1.1' + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + version = '1.2' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) + if self.download_url: + file.write('Download-URL: %s\n' % self.download_url) + + long_desc = rfc822_escape(self.get_long_description()) + file.write('Description: %s\n' % long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + file.write('Keywords: %s\n' % keywords) + + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + file.write('Requires-Python: %s\n' % self.python_requires) + + +# from Python 3.4 +def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree. """ - environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2) - if not environment_local: - return - - # from Python 3.4 - def write_pkg_info(self, base_dir): - """Write the PKG-INFO file into the release tree. - """ - with open(os.path.join(base_dir, 'PKG-INFO'), 'w', - encoding='UTF-8') as pkg_info: - self.write_pkg_file(pkg_info) - - distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: + self.write_pkg_file(pkg_info) -_patch_distribution_metadata_write_pkg_info() - sequence = tuple, list @@ -230,6 +205,9 @@ def check_packages(dist, attr, value): ) +_Distribution = get_unpatched(distutils.core.Distribution) + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -777,11 +755,6 @@ class Distribution(_Distribution): sys.stdout.detach(), encoding, errors, newline, line_buffering) -# Install it throughout the distutils -for module in distutils.dist, distutils.core, distutils.cmd: - module.Distribution = Distribution - - class Feature: """ **deprecated** -- The `Feature` facility was never completely implemented diff --git a/setuptools/extension.py b/setuptools/extension.py index 073d9459..03068d35 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -1,4 +1,3 @@ -import sys import re import functools import distutils.core @@ -8,11 +7,6 @@ import distutils.extension from setuptools.extern.six.moves import map from .monkey import get_unpatched -from . import msvc - -_Extension = get_unpatched(distutils.core.Extension) - -msvc.patch_for_specialized_compiler() def _have_cython(): @@ -33,6 +27,9 @@ def _have_cython(): have_pyrex = _have_cython +_Extension = get_unpatched(distutils.core.Extension) + + class Extension(_Extension): """Extension that uses '.c' files in place of '.pyx' files""" @@ -59,9 +56,3 @@ class Extension(_Extension): class Library(Extension): """Just like a regular Extension, but built as a library instead""" - - -distutils.core.Extension = Extension -distutils.extension.Extension = Extension -if 'distutils.command.build_ext' in sys.modules: - sys.modules['distutils.command.build_ext'].Extension = Extension 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.1 From 6a74dc955603fb6dcdaa2877b332c7ff1c55dfad Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 20:05:03 -0400 Subject: distutils will always be in globals --- setuptools/msvc.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 26e399cc..d7c88955 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -63,10 +63,6 @@ def patch_for_specialized_compiler(): # Compilers only availables on Microsoft Windows return - if 'distutils' not in globals(): - # The module isn't available to be patched - return - if unpatched: # Already patched return -- cgit v1.2.1 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 ++++++++++++++++++++++++++++++++-- setuptools/msvc.py | 90 ++++++++++++++-------------------------------------- 2 files changed, 79 insertions(+), 70 deletions(-) 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 diff --git a/setuptools/msvc.py b/setuptools/msvc.py index d7c88955..10507ab2 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -1,6 +1,20 @@ """ -This module adds improved support for Microsoft Visual C++ compilers. +Improved support for Microsoft Visual C++ compilers. + +Known supported compilers: +-------------------------- +Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); + Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Windows SDK 6.1 (x86, x64, ia64) + +Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + +Microsoft Visual C++ 14.0: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) """ + import os import sys import platform @@ -10,6 +24,8 @@ from distutils.version import StrictVersion from setuptools.extern.six.moves import filterfalse +from . import monkey + if platform.system() == 'Windows': from setuptools.extern.six.moves import winreg safe_env = os.environ @@ -26,70 +42,10 @@ else: safe_env = dict() try: - # Distutil file for MSVC++ 9.0 and upper (Python 2.7 to 3.4) - import distutils.msvc9compiler as msvc9compiler + from distutils.msvc9compiler import Reg except ImportError: pass -try: - # Distutil file for MSVC++ 14.0 and upper (Python 3.5+) - import distutils._msvccompiler as msvc14compiler -except ImportError: - pass - - -unpatched = dict() - - -def patch_for_specialized_compiler(): - """ - Patch functions in distutils to use standalone Microsoft Visual C++ - compilers. - - Known supported compilers: - -------------------------- - Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); - Microsoft Windows SDK 6.1 (x86, x64, ia64) - - Microsoft Visual C++ 10.0: - Microsoft Windows SDK 7.1 (x86, x64, ia64) - - Microsoft Visual C++ 14.0: - Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) - """ - if platform.system() != 'Windows': - # 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 = 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 - def msvc9_find_vcvarsall(version): """ @@ -113,7 +69,6 @@ def msvc9_find_vcvarsall(version): ------ vcvarsall.bat path: str """ - Reg = msvc9compiler.Reg VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' key = VC_BASE % ('', version) try: @@ -132,7 +87,7 @@ def msvc9_find_vcvarsall(version): if os.path.isfile(vcvarsall): return vcvarsall - return unpatched['msvc9_find_vcvarsall'](version) + return monkey.unpatched['msvc9_find_vcvarsall'](version) def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): @@ -165,7 +120,8 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): """ # Try to get environement from vcvarsall.bat (Classical way) try: - return unpatched['msvc9_query_vcvarsall'](ver, arch, *args, **kwargs) + orig = monkey.unpatched['msvc9_query_vcvarsall'] + return orig(ver, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError: # Pass error if Vcvarsall.bat is missing pass @@ -204,7 +160,7 @@ def msvc14_get_vc_env(plat_spec): """ # Try to get environment from vcvarsall.bat (Classical way) try: - return unpatched['msvc14_get_vc_env'](plat_spec) + return monkey.unpatched['msvc14_get_vc_env'](plat_spec) except distutils.errors.DistutilsPlatformError: # Pass error Vcvarsall.bat is missing pass @@ -227,7 +183,7 @@ def msvc14_gen_lib_options(*args, **kwargs): import numpy as np if StrictVersion(np.__version__) < StrictVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) - return unpatched['msvc14_gen_lib_options'](*args, **kwargs) + return monkey.unpatched['msvc14_gen_lib_options'](*args, **kwargs) def _augment_exception(exc, version, arch=''): -- cgit v1.2.1 From 634a3c9e360812480646dc0baa1035bfc75137f6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 20:31:42 -0400 Subject: Update changelog --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a45575ac..385fd07f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -24,6 +24,9 @@ v27.0.0 site, but the command is now deprecated. Users are urged to use Read The Docs instead. +* Introduce the ``monkey`` module to encapsulate the distutils + monkeypatching behavior. + v26.1.1 ------- -- cgit v1.2.1 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(-) 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.1 From fa187c39baf5fa0fee1feb3568177afb244ac30b Mon Sep 17 00:00:00 2001 From: Valentin Valls Date: Mon, 5 Sep 2016 09:34:22 +0200 Subject: Use LooseVersion instread of StrictVersion --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 26e399cc..c7dd7976 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -6,7 +6,7 @@ import sys import platform import itertools import distutils.errors -from distutils.version import StrictVersion +from distutils.version import LooseVersion from setuptools.extern.six.moves import filterfalse @@ -229,7 +229,7 @@ def msvc14_gen_lib_options(*args, **kwargs): """ if "numpy.distutils" in sys.modules: import numpy as np - if StrictVersion(np.__version__) < StrictVersion('1.11.2'): + if LooseVersion(np.__version__) < LooseVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) return unpatched['msvc14_gen_lib_options'](*args, **kwargs) -- cgit v1.2.1 From 54343cdaee7de324b15f3ace93657f34a53c5e34 Mon Sep 17 00:00:00 2001 From: Valentin Valls Date: Tue, 6 Sep 2016 11:10:34 +0200 Subject: Use LegacyVersion instead of LooseVersion - Also apply the patch for numpy 1.11.2 pre release --- setuptools/msvc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/msvc.py b/setuptools/msvc.py index c7dd7976..ecbea818 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -6,7 +6,7 @@ import sys import platform import itertools import distutils.errors -from distutils.version import LooseVersion +from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse @@ -229,7 +229,7 @@ def msvc14_gen_lib_options(*args, **kwargs): """ if "numpy.distutils" in sys.modules: import numpy as np - if LooseVersion(np.__version__) < LooseVersion('1.11.2'): + if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) return unpatched['msvc14_gen_lib_options'](*args, **kwargs) -- cgit v1.2.1 From dd0f44984d23f8953affce76fed4735a04c60803 Mon Sep 17 00:00:00 2001 From: Leonardo Rochael Almeida Date: Thu, 8 Sep 2016 11:54:38 -0300 Subject: pytest: ignore all .* directories In Cloud9 a .c9 directory is created. Other IDEs like to add .directories as well --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 640716a6..b35abfa9 100755 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py -norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .tox +norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .* flake8-ignore = setuptools/site-patch.py F821 setuptools/py*compat.py F811 -- cgit v1.2.1 From 00ac09a54693e77cbe791d0f8ca85db20a547e14 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Fri, 9 Sep 2016 07:20:49 -0400 Subject: Use EXT_SUFFIX instead of SO for py_limited_api renaming --- setuptools/command/build_ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index f994b626..454c91fb 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -111,7 +111,7 @@ class build_ext(_build_ext): and get_abi3_suffix() ) if use_abi3: - so_ext = get_config_var('SO') + so_ext = get_config_var('EXT_SUFFIX') filename = filename[:-len(so_ext)] filename = filename + get_abi3_suffix() if isinstance(ext, Library): -- cgit v1.2.1 From 18f4f4f56e4dfeeb152e86b4a84f84256ff0b57a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 09:57:04 -0400 Subject: Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a45575ac..9a48c1cb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,7 @@ CHANGES ======= + v27.0.0 ------- @@ -24,6 +25,11 @@ v27.0.0 site, but the command is now deprecated. Users are urged to use Read The Docs instead. +* #776: Use EXT_SUFFIX for py_limited_api renaming. + +* #774 and #775: Use LegacyVersion from packaging when + detecting numpy versions. + v26.1.1 ------- -- cgit v1.2.1 From c550d2d91f19e42257926f108bc269a759a2a13e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:04:29 -0400 Subject: =?UTF-8?q?Bump=20version:=2026.1.1=20=E2=86=92=2027.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 3bcd66a9..655b8f21 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 26.1.1 +current_version = 27.0.0 commit = True tag = True diff --git a/setup.py b/setup.py index 90702c1d..40977fe4 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="26.1.1", + version="27.0.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From c5645385252c45575569a01622405115d49cd1b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:13:36 -0400 Subject: Remove unused import --- setuptools/dist.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index d321e6c5..2f7bb59a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -2,7 +2,6 @@ __all__ = ['Distribution'] import re import os -import sys import warnings import numbers import distutils.log -- cgit v1.2.1 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/dist.py | 5 +++++ setuptools/monkey.py | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 2f7bb59a..b004f928 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -22,6 +22,11 @@ from setuptools.monkey import get_unpatched import pkg_resources +def _get_unpatched(cls): + warnings.warn("Do not call this function", DeprecationWarning) + return get_unpatched(cls) + + # Based on Python 3.5 version def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. 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.1 From 857c16bbb93e0293a21a8c00a421cc6ebe63614d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:28:21 -0400 Subject: Move changelog following v27 release --- CHANGES.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 385fd07f..4fec8a96 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,12 @@ CHANGES ======= +v27.1.0 +------- + +* Introduce the (private) ``monkey`` module to encapsulate + the distutils monkeypatching behavior. + v27.0.0 ------- @@ -24,9 +30,6 @@ v27.0.0 site, but the command is now deprecated. Users are urged to use Read The Docs instead. -* Introduce the ``monkey`` module to encapsulate the distutils - monkeypatching behavior. - v26.1.1 ------- -- cgit v1.2.1 From 1aa1d6c94b108147030fdf11057df3251b7eefda Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:51:34 -0400 Subject: =?UTF-8?q?Bump=20version:=2027.0.0=20=E2=86=92=2027.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 655b8f21..15570e34 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 27.0.0 +current_version = 27.1.0 commit = True tag = True diff --git a/setup.py b/setup.py index 40977fe4..a4d5f607 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="27.0.0", + version="27.1.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From a343901877cfe4fa922c40076f3c16aa59d4b265 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 11:05:18 -0400 Subject: Remove unused import --- setuptools/tests/test_msvc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index 14e0f208..a0c76ea0 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -6,8 +6,6 @@ import os import contextlib import distutils.errors -from setuptools.extern import six - import pytest try: from unittest import mock -- cgit v1.2.1 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. --- CHANGES.rst | 5 +++++ setuptools/monkey.py | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 35e69661..58a36db7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v27.1.1 +------- + +* #778: Fix MSVC monkeypatching. + v27.1.0 ------- 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.1 From a5eba2162977f28450bbc7c53b09f2272d7c118a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 11:24:44 -0400 Subject: =?UTF-8?q?Bump=20version:=2027.1.0=20=E2=86=92=2027.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 15570e34..cb99a56e 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 27.1.0 +current_version = 27.1.1 commit = True tag = True diff --git a/setup.py b/setup.py index a4d5f607..c81ddaf1 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="27.1.0", + version="27.1.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 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(-) 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.1 From 93443d79b18d4c318de366dc6ba585b2e9476d04 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 11:56:44 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 58a36db7..99de2684 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v27.1.2 +------- + +* #779 via #781: Fix circular import. + v27.1.1 ------- -- cgit v1.2.1 From 5cc0ec25c0ef816de01b7416aa6bef172f91566d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 11:57:25 -0400 Subject: =?UTF-8?q?Bump=20version:=2027.1.1=20=E2=86=92=2027.1.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index cb99a56e..69dd6151 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 27.1.1 +current_version = 27.1.2 commit = True tag = True diff --git a/setup.py b/setup.py index c81ddaf1..b2b1a0e1 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="27.1.1", + version="27.1.2", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 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 ++++++++++++++++++++------------- setuptools/msvc.py | 10 +++++----- setuptools/py26compat.py | 7 +++++++ 3 files changed, 32 insertions(+), 18 deletions(-) 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 diff --git a/setuptools/msvc.py b/setuptools/msvc.py index a902a4a7..ae5a2b6a 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -24,7 +24,7 @@ from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse -from . import monkey +from .monkey import get_unpatched_func if platform.system() == 'Windows': from setuptools.extern.six.moves import winreg @@ -87,7 +87,7 @@ def msvc9_find_vcvarsall(version): if os.path.isfile(vcvarsall): return vcvarsall - return monkey.unpatched['msvc9_find_vcvarsall'](version) + return get_unpatched_func(msvc9_find_vcvarsall)(version) def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): @@ -120,7 +120,7 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): """ # Try to get environement from vcvarsall.bat (Classical way) try: - orig = monkey.unpatched['msvc9_query_vcvarsall'] + orig = get_unpatched_func(msvc9_query_vcvarsall) return orig(ver, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError: # Pass error if Vcvarsall.bat is missing @@ -160,7 +160,7 @@ def msvc14_get_vc_env(plat_spec): """ # Try to get environment from vcvarsall.bat (Classical way) try: - return monkey.unpatched['msvc14_get_vc_env'](plat_spec) + return get_unpatched_func(msvc14_get_vc_env)(plat_spec) except distutils.errors.DistutilsPlatformError: # Pass error Vcvarsall.bat is missing pass @@ -183,7 +183,7 @@ def msvc14_gen_lib_options(*args, **kwargs): import numpy as np if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) - return monkey.unpatched['msvc14_gen_lib_options'](*args, **kwargs) + return get_unpatched_func(msvc14_gen_lib_options)(*args, **kwargs) def _augment_exception(exc, version, arch=''): diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py index 90cd695a..5778cdf1 100644 --- a/setuptools/py26compat.py +++ b/setuptools/py26compat.py @@ -22,3 +22,10 @@ def strip_fragment(url): if sys.version_info >= (2, 7): strip_fragment = lambda x: x + + +try: + from importlib import import_module +except ImportError: + def import_module(module_name): + return __import__(module_name, fromlist=['__name__']) -- cgit v1.2.1 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(-) 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.1 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 ++++++++++++-- setuptools/msvc.py | 10 +++++----- 2 files changed, 17 insertions(+), 7 deletions(-) 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') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index ae5a2b6a..e9665e10 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -24,7 +24,7 @@ from pkg_resources.extern.packaging.version import LegacyVersion from setuptools.extern.six.moves import filterfalse -from .monkey import get_unpatched_func +from .monkey import get_unpatched if platform.system() == 'Windows': from setuptools.extern.six.moves import winreg @@ -87,7 +87,7 @@ def msvc9_find_vcvarsall(version): if os.path.isfile(vcvarsall): return vcvarsall - return get_unpatched_func(msvc9_find_vcvarsall)(version) + return get_unpatched(msvc9_find_vcvarsall)(version) def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): @@ -120,7 +120,7 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): """ # Try to get environement from vcvarsall.bat (Classical way) try: - orig = get_unpatched_func(msvc9_query_vcvarsall) + orig = get_unpatched(msvc9_query_vcvarsall) return orig(ver, arch, *args, **kwargs) except distutils.errors.DistutilsPlatformError: # Pass error if Vcvarsall.bat is missing @@ -160,7 +160,7 @@ def msvc14_get_vc_env(plat_spec): """ # Try to get environment from vcvarsall.bat (Classical way) try: - return get_unpatched_func(msvc14_get_vc_env)(plat_spec) + return get_unpatched(msvc14_get_vc_env)(plat_spec) except distutils.errors.DistutilsPlatformError: # Pass error Vcvarsall.bat is missing pass @@ -183,7 +183,7 @@ def msvc14_gen_lib_options(*args, **kwargs): import numpy as np if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) - return get_unpatched_func(msvc14_gen_lib_options)(*args, **kwargs) + return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) def _augment_exception(exc, version, arch=''): -- cgit v1.2.1 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(-) 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.1 From 2a7a7179ca18b1c97e435726e62c3ab0227fb20e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 12:57:02 -0400 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 99de2684..da6322a2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +v27.2.0 +------- + +* Nicer, more consistent interfaces for msvc monkeypatching. + v27.1.2 ------- -- cgit v1.2.1 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(-) 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.1 From 1f23f9a25e6c91554954185e84497056062093be Mon Sep 17 00:00:00 2001 From: Steve Kowalik Date: Tue, 13 Sep 2016 10:54:45 +1200 Subject: Don't duplicate error case in package_index easy_install has code to handle parsing a requirement, catching the ValueError and then raising a DistUtilsError. This code was entirely duplicated in package_index, so I've slightly refactored to remove the duplication. --- setuptools/command/easy_install.py | 14 +++----------- setuptools/package_index.py | 17 ++++++++++------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index e8b90c70..a3792ce2 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -49,8 +49,9 @@ from setuptools.sandbox import run_setup from setuptools.py31compat import get_path, get_config_vars from setuptools.command import setopt from setuptools.archive_util import unpack_archive -from setuptools.package_index import PackageIndex -from setuptools.package_index import URL_SCHEME +from setuptools.package_index import ( + PackageIndex, parse_requirement_arg, URL_SCHEME, +) from setuptools.command import bdist_egg, egg_info from pkg_resources import ( yield_lines, normalize_path, resource_string, ensure_directory, @@ -1522,15 +1523,6 @@ def get_exe_prefixes(exe_filename): return prefixes -def parse_requirement_arg(spec): - try: - return Requirement.parse(spec) - except ValueError: - raise DistutilsError( - "Not a URL, existing file, or requirement spec: %r" % (spec,) - ) - - class PthDistributions(Environment): """A .pth file with Distribution paths in it""" diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 82cd608f..3fb39269 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -52,6 +52,15 @@ _tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" user_agent = _tmpl.format(py_major=sys.version[:3], **globals()) +def parse_requirement_arg(spec): + try: + return Requirement.parse(spec) + except ValueError: + raise DistutilsError( + "Not a URL, existing file, or requirement spec: %r" % (spec,) + ) + + def parse_bdist_wininst(name): """Return (base,pyversion) or (None,None) for possible .exe name""" @@ -561,13 +570,7 @@ class PackageIndex(Environment): # Existing file or directory, just return it return spec else: - try: - spec = Requirement.parse(spec) - except ValueError: - raise DistutilsError( - "Not a URL, existing file, or requirement spec: %r" % - (spec,) - ) + spec = parse_requirement_arg(spec) return getattr(self.fetch_distribution(spec, tmpdir), 'location', None) def fetch_distribution( -- cgit v1.2.1 From 11ef9a98f1da8c883fc346ce78afbad78d5eff83 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 14 Sep 2016 13:54:26 -0400 Subject: Suppress ValueError in fixup_namespace_packages. Fixes #520. Fixes #513. --- CHANGES.rst | 3 +++ pkg_resources/__init__.py | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index da6322a2..af25cc4a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ CHANGES v27.2.0 ------- +* #520 and #513: Suppress ValueErrors in fixup_namespace_packages + when lookup fails. + * Nicer, more consistent interfaces for msvc monkeypatching. v27.1.2 diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index a93a3da7..4208c4ec 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2066,6 +2066,15 @@ def _rebuild_mod_path(orig_path, package_name, module): """ sys_path = [_normalize_cached(p) for p in sys.path] + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + def position_in_sys_path(path): """ Return the ordinal of the path based on its position in sys.path @@ -2073,7 +2082,7 @@ def _rebuild_mod_path(orig_path, package_name, module): path_parts = path.split(os.sep) module_parts = package_name.count('.') + 1 parts = path_parts[:-module_parts] - return sys_path.index(_normalize_cached(os.sep.join(parts))) + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) orig_path.sort(key=position_in_sys_path) module.__path__[:] = [_normalize_cached(p) for p in orig_path] -- cgit v1.2.1 From 629ad3da717eb360eda53874e590b48f0a5acd2e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 14 Sep 2016 14:00:36 -0400 Subject: =?UTF-8?q?Bump=20version:=2027.1.2=20=E2=86=92=2027.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 69dd6151..ce161ffe 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 27.1.2 +current_version = 27.2.0 commit = True tag = True diff --git a/setup.py b/setup.py index b2b1a0e1..d04d9b5b 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="27.1.2", + version="27.2.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 53d8b6b69d9a24e53bf25d830124c4698fe44ce3 Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 18 Sep 2016 03:52:48 +0700 Subject: Upgrade pyparsing to version 2.1.9 --- pkg_resources/_vendor/pyparsing.py | 158 ++++++++++++++++++++++++++++++------- pkg_resources/_vendor/vendored.txt | 2 +- 2 files changed, 131 insertions(+), 29 deletions(-) mode change 100644 => 100755 pkg_resources/_vendor/pyparsing.py diff --git a/pkg_resources/_vendor/pyparsing.py b/pkg_resources/_vendor/pyparsing.py old mode 100644 new mode 100755 index 89cffc10..d602a35a --- a/pkg_resources/_vendor/pyparsing.py +++ b/pkg_resources/_vendor/pyparsing.py @@ -1,6 +1,6 @@ # module pyparsing.py # -# Copyright (c) 2003-2015 Paul T. McGuire +# Copyright (c) 2003-2016 Paul T. McGuire # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -31,15 +31,18 @@ vs. the traditional lex/yacc approach, or the use of regular expressions. With don't need to learn a new syntax for defining grammars or matching expressions - the parsing module provides a library of classes that you use to construct the grammar directly in Python. -Here is a program to parse "Hello, World!" (or any greeting of the form C{", !"}):: +Here is a program to parse "Hello, World!" (or any greeting of the form +C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements +(L{'+'} operator gives L{And} expressions, strings are auto-converted to +L{Literal} expressions):: from pyparsing import Word, alphas # define grammar of a greeting - greet = Word( alphas ) + "," + Word( alphas ) + "!" + greet = Word(alphas) + "," + Word(alphas) + "!" hello = "Hello, World!" - print (hello, "->", greet.parseString( hello )) + print (hello, "->", greet.parseString(hello)) The program outputs the following:: @@ -48,7 +51,7 @@ The program outputs the following:: The Python representation of the grammar is quite readable, owing to the self-explanatory class names, and the use of '+', '|' and '^' operators. -The parsed results returned from L{I{ParserElement.parseString}} can be accessed as a nested list, a dictionary, or an +The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an object with named attributes. The pyparsing module handles some of the problems that are typically vexing when writing text parsers: @@ -57,8 +60,8 @@ The pyparsing module handles some of the problems that are typically vexing when - embedded comments """ -__version__ = "2.1.8" -__versionTime__ = "14 Aug 2016 08:43 UTC" +__version__ = "2.1.9" +__versionTime__ = "10 Sep 2016 15:10 UTC" __author__ = "Paul McGuire " import string @@ -107,7 +110,7 @@ __all__ = [ 'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd', 'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute', 'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass', -'tokenMap', 'pyparsing_common', +'CloseMatch', 'tokenMap', 'pyparsing_common', ] system_version = tuple(sys.version_info)[:3] @@ -291,7 +294,7 @@ class _ParseResultsWithOffset(object): def __getitem__(self,i): return self.tup[i] def __repr__(self): - return repr(self.tup) + return repr(self.tup[0]) def setOffset(self,i): self.tup = (self.tup[0],i) @@ -310,6 +313,7 @@ class ParseResults(object): # equivalent form: # date_str = integer("year") + '/' + integer("month") + '/' + integer("day") + # parseString returns a ParseResults object result = date_str.parseString("1999/12/31") def test(s, fn=repr): @@ -836,8 +840,8 @@ class ParseResults(object): return None elif (len(self) == 1 and len(self.__tokdict) == 1 and - self.__tokdict.values()[0][0][1] in (0,-1)): - return self.__tokdict.keys()[0] + next(iter(self.__tokdict.values()))[0][1] in (0,-1)): + return next(iter(self.__tokdict.keys())) else: return None @@ -1775,7 +1779,15 @@ class ParserElement(object): def __add__(self, other ): """ - Implementation of + operator - returns C{L{And}} + Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement + converts them to L{Literal}s by default. + + Example:: + greet = Word(alphas) + "," + Word(alphas) + "!" + hello = "Hello, World!" + print (hello, "->", greet.parseString(hello)) + Prints:: + Hello, World! -> ['Hello', ',', 'World', '!'] """ if isinstance( other, basestring ): other = ParserElement._literalStringClass( other ) @@ -1972,7 +1984,7 @@ class ParserElement(object): def __call__(self, name=None): """ - Shortcut for C{L{setResultsName}}, with C{listAllMatches=default}. + Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}. If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be passed as C{True}. @@ -2083,7 +2095,8 @@ class ParserElement(object): Match alphaword at loc 15(1,16) Exception raised:Expected alphaword (at char 15), (line:1, col:16) - The output shown is that produced by the default debug actions. Prior to attempting + The output shown is that produced by the default debug actions - custom debug actions can be + specified using L{setDebugActions}. Prior to attempting to match the C{wd} expression, the debugging message C{"Match at loc (,)"} is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"} message is shown. Also note the use of L{setName} to assign a human-readable name to the expression, @@ -2393,8 +2406,10 @@ class Keyword(Token): """ DEFAULT_KEYWORD_CHARS = alphanums+"_$" - def __init__( self, matchString, identChars=DEFAULT_KEYWORD_CHARS, caseless=False ): + def __init__( self, matchString, identChars=None, caseless=False ): super(Keyword,self).__init__() + if identChars is None: + identChars = Keyword.DEFAULT_KEYWORD_CHARS self.match = matchString self.matchLen = len(matchString) try: @@ -2469,7 +2484,7 @@ class CaselessKeyword(Keyword): (Contrast with example for L{CaselessLiteral}.) """ - def __init__( self, matchString, identChars=Keyword.DEFAULT_KEYWORD_CHARS ): + def __init__( self, matchString, identChars=None ): super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True ) def parseImpl( self, instring, loc, doActions=True ): @@ -2478,6 +2493,67 @@ class CaselessKeyword(Keyword): return loc+self.matchLen, self.match raise ParseException(instring, loc, self.errmsg, self) +class CloseMatch(Token): + """ + A variation on L{Literal} which matches "close" matches, that is, + strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters: + - C{match_string} - string to be matched + - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match + + The results from a successful parse will contain the matched text from the input string and the following named results: + - C{mismatches} - a list of the positions within the match_string where mismatches were found + - C{original} - the original match_string used to compare against the input string + + If C{mismatches} is an empty list, then the match was an exact match. + + Example:: + patt = CloseMatch("ATCATCGAATGGA") + patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) + patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) + + # exact match + patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) + + # close match allowing up to 2 mismatches + patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2) + patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) + """ + def __init__(self, match_string, maxMismatches=1): + super(CloseMatch,self).__init__() + self.name = match_string + self.match_string = match_string + self.maxMismatches = maxMismatches + self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches) + self.mayIndexError = False + self.mayReturnEmpty = False + + def parseImpl( self, instring, loc, doActions=True ): + start = loc + instrlen = len(instring) + maxloc = start + len(self.match_string) + + if maxloc <= instrlen: + match_string = self.match_string + match_stringloc = 0 + mismatches = [] + maxMismatches = self.maxMismatches + + for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)): + src,mat = s_m + if src != mat: + mismatches.append(match_stringloc) + if len(mismatches) > maxMismatches: + break + else: + loc = match_stringloc + 1 + results = ParseResults([instring[start:loc]]) + results['original'] = self.match_string + results['mismatches'] = mismatches + return loc, results + + raise ParseException(instring, loc, self.errmsg, self) + + class Word(Token): """ Token for matching words composed of allowed character sets. @@ -2646,7 +2722,7 @@ class Regex(Token): Example:: realnum = Regex(r"[+-]?\d+\.\d*") - date = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)') + date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)') # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") """ @@ -4274,7 +4350,10 @@ class OnlyOnce(object): def traceParseAction(f): """ - Decorator for debugging parse actions. + Decorator for debugging parse actions. + + When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".} + When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised. Example:: wd = Word(alphas) @@ -4339,9 +4418,16 @@ def countedArray( expr, intExpr=None ): integer expr expr expr... where the leading integer tells how many expr expressions follow. The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed. + + If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value. Example:: countedArray(Word(alphas)).parseString('2 ab cd ef') # -> ['ab', 'cd'] + + # in this parser, the leading integer value is given in binary, + # '10' indicating that 2 values are in the array + binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2)) + countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef') # -> ['ab', 'cd'] """ arrayExpr = Forward() def countFieldParseAction(s,l,t): @@ -4726,10 +4812,10 @@ def tokenMap(func, *args): return pa upcaseTokens = tokenMap(lambda t: _ustr(t).upper()) -"""Helper parse action to convert tokens to upper case.""" +"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}""" downcaseTokens = tokenMap(lambda t: _ustr(t).lower()) -"""Helper parse action to convert tokens to lower case.""" +"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}""" def _makeTags(tagStr, xml): """Internal helper to construct opening and closing tag expressions, given a tag name""" @@ -4921,7 +5007,7 @@ def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): Example:: # simple example of four-function arithmetic with ints and variable names - integer = pyparsing_common.signedInteger + integer = pyparsing_common.signed_integer varname = pyparsing_common.identifier arith_expr = infixNotation(integer | varname, @@ -5241,23 +5327,27 @@ _commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + Optional( Word(" \t") + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") -"""Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" # some other useful expressions - using lower-case class name since we are really using this as a namespace class pyparsing_common: """ Here are some common low-level expressions that may be useful in jump-starting parser development: - - numeric forms (L{integers}, L{reals}, L{scientific notation}) + - numeric forms (L{integers}, L{reals}, L{scientific notation}) - common L{programming identifiers} - network addresses (L{MAC}, L{IPv4}, L{IPv6}) - ISO8601 L{dates} and L{datetime} - L{UUID} + - L{comma-separated list} Parse actions: - C{L{convertToInteger}} - C{L{convertToFloat}} - C{L{convertToDate}} - C{L{convertToDatetime}} - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} Example:: pyparsing_common.number.runTests(''' @@ -5393,25 +5483,25 @@ class pyparsing_common: hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) """expression that parses a hexadecimal integer, returns an int""" - signedInteger = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) """expression that parses an integer with optional leading sign, returns an int""" - fraction = (signedInteger().setParseAction(convertToFloat) + '/' + signedInteger().setParseAction(convertToFloat)).setName("fraction") + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") """fractional expression of an integer divided by an integer, returns a float""" fraction.addParseAction(lambda t: t[0]/t[-1]) - mixed_integer = (fraction | signedInteger + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" mixed_integer.addParseAction(sum) real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) """expression that parses a floating point number and returns a float""" - sciReal = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) """expression that parses a floating point number with optional scientific notation and returns a float""" # streamlining this expression makes the docs nicer-looking - number = (sciReal | real | signedInteger).streamline() + number = (sci_real | real | signed_integer).streamline() """any numeric expression, returns the corresponding Python type""" fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) @@ -5503,6 +5593,18 @@ class pyparsing_common: """ return pyparsing_common._html_stripper.transformString(tokens[0]) + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + if __name__ == "__main__": selectToken = CaselessLiteral("select") diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index 2b51c547..22e1309c 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -1,4 +1,4 @@ packaging==16.7 -pyparsing==2.1.8 +pyparsing==2.1.9 six==1.10.0 appdirs==1.4.0 -- cgit v1.2.1 From 5e4eea7d600f44321e76689890f9f885669f34c9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 17 Sep 2016 17:05:07 -0400 Subject: In test command, add installed eggs to PYTHONPATH when invoking tests so that subprocesses will also have the dependencies available. Fixes #794. --- CHANGES.rst | 7 ++++++ setuptools/command/test.py | 57 ++++++++++++++++++++++++++++++++++++++-------- setuptools/dist.py | 1 + 3 files changed, 56 insertions(+), 9 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index af25cc4a..54a4d52a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,13 @@ CHANGES ======= +v27.3.0 +------- + +* #794: In test command, add installed eggs to PYTHONPATH + when invoking tests so that subprocesses will also have the + dependencies available. + v27.2.0 ------- diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 2d1adba8..e0650d27 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -1,10 +1,12 @@ +import os +import operator import sys import contextlib from distutils.errors import DistutilsOptionError from unittest import TestLoader from setuptools.extern import six -from setuptools.extern.six.moves import map +from setuptools.extern.six.moves import map, filter from pkg_resources import (resource_listdir, resource_exists, normalize_path, working_set, _namespace_packages, @@ -112,7 +114,7 @@ class test(Command): func() @contextlib.contextmanager - def project_on_sys_path(self): + def project_on_sys_path(self, include_dists=[]): with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False) if with_2to3: @@ -144,23 +146,57 @@ class test(Command): old_modules = sys.modules.copy() try: - sys.path.insert(0, normalize_path(ei_cmd.egg_base)) + project_path = normalize_path(ei_cmd.egg_base) + sys.path.insert(0, project_path) working_set.__init__() add_activation_listener(lambda dist: dist.activate()) require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) - yield + with self.paths_on_pythonpath([project_path]): + yield finally: sys.path[:] = old_path sys.modules.clear() sys.modules.update(old_modules) working_set.__init__() + @staticmethod + @contextlib.contextmanager + def paths_on_pythonpath(paths): + """ + Add the indicated paths to the head of the PYTHONPATH environment + variable so that subprocesses will also see the packages at + these paths. + + Do this in a context that restores the value on exit. + """ + nothing = object() + orig_pythonpath = os.environ.get('PYTHONPATH', nothing) + current_pythonpath = os.environ.get('PYTHONPATH', '') + try: + prefix = os.pathsep.join(paths) + to_join = filter(None, [prefix, current_pythonpath]) + new_path = os.pathsep.join(to_join) + if new_path: + os.environ['PYTHONPATH'] = new_path + yield + finally: + if orig_pythonpath is nothing: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = orig_pythonpath + def run(self): + installed_dists = [] if self.distribution.install_requires: - self.distribution.fetch_build_eggs( - self.distribution.install_requires) + installed_dists.extend( + self.distribution.fetch_build_eggs( + self.distribution.install_requires, + )) if self.distribution.tests_require: - self.distribution.fetch_build_eggs(self.distribution.tests_require) + installed_dists.extend( + self.distribution.fetch_build_eggs( + self.distribution.tests_require, + )) cmd = ' '.join(self._argv) if self.dry_run: @@ -168,8 +204,11 @@ class test(Command): return self.announce('running "%s"' % cmd) - with self.project_on_sys_path(): - self.run_tests() + + paths = map(operator.attrgetter('location'), installed_dists) + with self.paths_on_pythonpath(paths): + with self.project_on_sys_path(): + self.run_tests() def run_tests(self): # Purge modules under test from sys.modules. The test loader will diff --git a/setuptools/dist.py b/setuptools/dist.py index b004f928..364f2b4d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -362,6 +362,7 @@ class Distribution(_Distribution): ) for dist in resolved_dists: pkg_resources.working_set.add(dist, replace=True) + return resolved_dists def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From 55c36fdfa8fdc3b4581e0331cc91a069a001da51 Mon Sep 17 00:00:00 2001 From: stepshal Date: Sun, 18 Sep 2016 04:25:06 +0700 Subject: Add missing whitespace. --- setuptools/package_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 3fb39269..3e8d6818 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -293,7 +293,7 @@ class PackageIndex(Environment): ca_bundle=None, verify_ssl=True, *args, **kw ): Environment.__init__(self, *args, **kw) - self.index_url = index_url + "/"[:not index_url.endswith('/')] + self.index_url = index_url + "/" [:not index_url.endswith('/')] self.scanned_urls = {} self.fetched_urls = {} self.package_pages = {} -- cgit v1.2.1 From 2f3b7b204187a1c93c391ffac96a9220cbb57f91 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Sep 2016 09:20:17 -0400 Subject: Extract test.install_dists and distill it with a variable extraction and fallback variables. --- CHANGES.rst | 3 ++- setuptools/command/test.py | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 54a4d52a..8af3b938 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,8 @@ v27.3.0 * #794: In test command, add installed eggs to PYTHONPATH when invoking tests so that subprocesses will also have the - dependencies available. + dependencies available. Fixes `tox 330 + `_. v27.2.0 ------- diff --git a/setuptools/command/test.py b/setuptools/command/test.py index e0650d27..48d5b5e1 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -2,6 +2,7 @@ import os import operator import sys import contextlib +import itertools from distutils.errors import DistutilsOptionError from unittest import TestLoader @@ -185,18 +186,18 @@ class test(Command): else: os.environ['PYTHONPATH'] = orig_pythonpath + def install_dists(self): + """ + Install the requirements indicated by self.distribution and + return an iterable of the dists that were built. + """ + dist = self.distribution + ir_d = dist.fetch_build_eggs(dist.install_requires or []) + tr_d = dist.fetch_build_eggs(dist.tests_require or []) + return itertools.chain(ir_d, tr_d) + def run(self): - installed_dists = [] - if self.distribution.install_requires: - installed_dists.extend( - self.distribution.fetch_build_eggs( - self.distribution.install_requires, - )) - if self.distribution.tests_require: - installed_dists.extend( - self.distribution.fetch_build_eggs( - self.distribution.tests_require, - )) + installed_dists = self.install_dists() cmd = ' '.join(self._argv) if self.dry_run: -- cgit v1.2.1 From 2148592395ef4da0d408be26bf5839117ee0e5fe Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Sep 2016 09:27:23 -0400 Subject: Even better, use a static method --- setuptools/command/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 48d5b5e1..38bbcd8b 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -186,18 +186,18 @@ class test(Command): else: os.environ['PYTHONPATH'] = orig_pythonpath - def install_dists(self): + @staticmethod + def install_dists(dist): """ Install the requirements indicated by self.distribution and return an iterable of the dists that were built. """ - dist = self.distribution ir_d = dist.fetch_build_eggs(dist.install_requires or []) tr_d = dist.fetch_build_eggs(dist.tests_require or []) return itertools.chain(ir_d, tr_d) def run(self): - installed_dists = self.install_dists() + installed_dists = self.install_dists(self.distribution) cmd = ' '.join(self._argv) if self.dry_run: -- cgit v1.2.1 From 560e787eec3b5801de158eccd492f26d2440051e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 20 Sep 2016 21:18:44 -0400 Subject: Update changelog --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 8af3b938..19452d4f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ v27.3.0 dependencies available. Fixes `tox 330 `_. +* #795: Update vendored pyparsing 2.1.9. + v27.2.0 ------- -- cgit v1.2.1 From f773e19f4f85fae7312de57d6995fcb7c4795a30 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 20 Sep 2016 21:18:51 -0400 Subject: =?UTF-8?q?Bump=20version:=2027.2.0=20=E2=86=92=2027.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index ce161ffe..2c6fa292 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 27.2.0 +current_version = 27.3.0 commit = True tag = True diff --git a/setup.py b/setup.py index d04d9b5b..b16cef30 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="27.2.0", + version="27.3.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From aac208c1c711c5819024f88890e244dd18801feb Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Sun, 14 Aug 2016 05:59:35 +0200 Subject: Do not search excluded directories for packages Previously, PackageFinder.find would search the whole directory tree looking for packages, then remove excluded packages from this list. This made building a package very slow under some circumstances where the file tree was large. This change stops PackageFinder.find from descending in to directories that will never be included. --- setuptools/__init__.py | 83 +++++++++++++++++++++----------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 41b590d7..892626e6 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -7,7 +7,7 @@ import distutils.filelist from distutils.util import convert_path from fnmatch import fnmatchcase -from setuptools.extern.six.moves import filterfalse, map +from setuptools.extern.six.moves import filter, filterfalse, map import setuptools.version from setuptools.extension import Extension @@ -32,13 +32,18 @@ lib2to3_fixer_packages = ['lib2to3.fixes'] class PackageFinder(object): + """ + Generate a list of all Python packages found within a directory + """ @classmethod def find(cls, where='.', exclude=(), include=('*',)): """Return a list all Python packages found within directory 'where' - 'where' should be supplied as a "cross-platform" (i.e. URL-style) - path; it will be converted to the appropriate local path syntax. + 'where' is the root directory which will be searched for packages. It + should be supplied as a "cross-platform" (i.e. URL-style) path; it will + be converted to the appropriate local path syntax. + 'exclude' is a sequence of package names to exclude; '*' can be used as a wildcard in the names, such that 'foo.*' will exclude all subpackages of 'foo' (but not 'foo' itself). @@ -47,65 +52,47 @@ class PackageFinder(object): specified, only the named packages will be included. If it's not specified, all found packages will be included. 'include' can contain shell style wildcard patterns just like 'exclude'. - - The list of included packages is built up first and then any - explicitly excluded packages are removed from it. - """ - out = cls._find_packages_iter(convert_path(where)) - out = cls.require_parents(out) - includes = cls._build_filter(*include) - excludes = cls._build_filter('ez_setup', '*__pycache__', *exclude) - out = filter(includes, out) - out = filterfalse(excludes, out) - return list(out) - - @staticmethod - def require_parents(packages): """ - Exclude any apparent package that apparently doesn't include its - parent. - For example, exclude 'foo.bar' if 'foo' is not present. - """ - found = [] - for pkg in packages: - base, sep, child = pkg.rpartition('.') - if base and base not in found: - continue - found.append(pkg) - yield pkg + return list(cls._find_packages_iter( + convert_path(where), + cls._build_filter('ez_setup', '*__pycache__', *exclude), + cls._build_filter(*include))) - @staticmethod - def _candidate_dirs(base_path): + @classmethod + def _find_packages_iter(cls, where, exclude, include): """ - Return all dirs in base_path that might be packages. + All the packages found in 'where' that pass the 'include' filter, but + not the 'exclude' filter. """ - has_dot = lambda name: '.' in name - for root, dirs, files in os.walk(base_path, followlinks=True): - # Exclude directories that contain a period, as they cannot be - # packages. Mutate the list to avoid traversal. - dirs[:] = filterfalse(has_dot, dirs) - for dir in dirs: - yield os.path.relpath(os.path.join(root, dir), base_path) - - @classmethod - def _find_packages_iter(cls, base_path): - candidates = cls._candidate_dirs(base_path) - return ( - path.replace(os.path.sep, '.') - for path in candidates - if cls._looks_like_package(os.path.join(base_path, path)) - ) + for root, dirs, files in os.walk(where, followlinks=True): + # Copy dirs to iterate over it, then empty dirs. + all_dirs = dirs[:] + dirs[:] = [] + + for dir in all_dirs: + full_path = os.path.join(root, dir) + rel_path = os.path.relpath(full_path, where) + package = rel_path.replace(os.path.sep, '.') + + # Check if the directory is a package and passes the filters + if ('.' not in dir + and include(package) + and not exclude(package) + and cls._looks_like_package(full_path)): + yield package + dirs.append(dir) @staticmethod def _looks_like_package(path): + """Does a directory look like a package?""" return os.path.isfile(os.path.join(path, '__init__.py')) @staticmethod def _build_filter(*patterns): """ Given a list of patterns, return a callable that will be true only if - the input matches one of the patterns. + the input matches at least one of the patterns. """ return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) -- cgit v1.2.1 From 75a78dc2feedb9287155f146d0b855ea46924961 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Thu, 22 Sep 2016 10:16:40 +1000 Subject: Update changelog --- CHANGES.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 19452d4f..d115533a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,11 @@ CHANGES ======= +In development +-------------- + +* #733: Do not search excluded directories for packages. + v27.3.0 ------- -- cgit v1.2.1 From 0e1b6fe0004764dcc4adc45bc0bb2665f5bedd04 Mon Sep 17 00:00:00 2001 From: Gabi Davar Date: Sat, 24 Sep 2016 16:24:37 +0300 Subject: certifi 2016.8.31 (#797) --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index b16cef30..22bdae10 100755 --- a/setup.py +++ b/setup.py @@ -167,11 +167,11 @@ setup_params = dict( """).strip().splitlines(), extras_require={ "ssl:sys_platform=='win32'": "wincertstore==0.2", - "certs": "certifi==2016.8.8", + "certs": "certifi==2016.8.31", }, dependency_links=[ pypi_link( - 'certifi-2016.8.8.tar.gz#md5=b57513f7670482da45bb350b792f659e', + 'certifi-2016.8.31.tar.gz#md5=2f22d484a36d38d98be74f9eeb2846ec', ), pypi_link( 'wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2', -- cgit v1.2.1 From 992adf2f5684e6660335d616149e050b3eaaed17 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Mon, 26 Sep 2016 14:00:00 +1000 Subject: Note find_packages backwards incompatible change Also add a test for the new behaviour. --- CHANGES.rst | 4 ++++ setuptools/tests/test_find_packages.py | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d115533a..cf8bcaf9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,10 @@ In development -------------- * #733: Do not search excluded directories for packages. + This introduced a backwards incompatible change in ``find_packages()`` + so that ``find_packages(exclude=['foo']) == []``, excluding subpackages of ``foo``. + Previously, ``find_packages(exclude=['foo']) == ['foo.bar']``, + even though the parent ``foo`` package was excluded. v27.3.0 ------- diff --git a/setuptools/tests/test_find_packages.py b/setuptools/tests/test_find_packages.py index df51b04f..9d31ccd7 100644 --- a/setuptools/tests/test_find_packages.py +++ b/setuptools/tests/test_find_packages.py @@ -98,6 +98,15 @@ class TestFindPackages: packages = find_packages(self.dist_dir, exclude=('pkg.*',)) assert packages == ['pkg'] + def test_exclude_recursive(self): + """ + Excluding a parent package should exclude all child packages as well. + """ + self._touch('__init__.py', self.pkg_dir) + self._touch('__init__.py', self.sub_pkg_dir) + packages = find_packages(self.dist_dir, exclude=('pkg',)) + assert packages == [] + def test_include_excludes_other(self): """ If include is specified, other packages should be excluded. -- cgit v1.2.1 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. --- CHANGES.rst | 9 ++++++++ setuptools/monkey.py | 64 +++++++++++++++++++++++++++++++--------------------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 19452d4f..0246c461 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,15 @@ CHANGES ======= +v27.3.1 +------- + +* #790: In MSVC monkeypatching, explicitly patch each + function by name in the target module instead of inferring + the module from the function's ``__module__``. Improves + compatibility with other packages that might have previously + patched distutils functions (i.e. NumPy). + v27.3.0 ------- 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.1 From e91f0b18021ba6ba073d9db19d4b5afb91f3f55e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 13:45:29 -0500 Subject: Update changelog --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d115533a..1861cb6b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,8 @@ In development * #733: Do not search excluded directories for packages. +* #795: Bump certifi. + v27.3.0 ------- -- cgit v1.2.1 From 394318306bac8e5cab251ca4cb099c40aebc97fd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 13:46:49 -0500 Subject: =?UTF-8?q?Bump=20version:=2027.3.0=20=E2=86=92=2027.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 2c6fa292..549aff16 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 27.3.0 +current_version = 27.3.1 commit = True tag = True diff --git a/setup.py b/setup.py index b16cef30..67c66b3c 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="27.3.0", + version="27.3.1", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", -- cgit v1.2.1 From 441e50c2eb29078d5b42046e14834837ffac9c9f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 13:57:39 -0500 Subject: Define version in changelog --- CHANGES.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 11d9ca93..0ce8e52b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,8 +2,8 @@ CHANGES ======= -In development --------------- +v28.0.0 +------- * #733: Do not search excluded directories for packages. This introduced a backwards incompatible change in ``find_packages()`` -- cgit v1.2.1 From 2b5937f56b9b1d8d91c1247540f41437ba99016e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 13:58:38 -0500 Subject: Remove Mercurial metadata --- .hgignore | 15 --- .hgtags | 308 -------------------------------------------------------------- 2 files changed, 323 deletions(-) delete mode 100644 .hgignore delete mode 100644 .hgtags diff --git a/.hgignore b/.hgignore deleted file mode 100644 index ebc53b33..00000000 --- a/.hgignore +++ /dev/null @@ -1,15 +0,0 @@ -syntax: glob -bin -build -dist -include -lib -distribute.egg-info -setuptools.egg-info -.coverage -.tox -*.egg -*.py[cod] -*.swp -*~ -.git* diff --git a/.hgtags b/.hgtags deleted file mode 100644 index 88f2dc60..00000000 --- a/.hgtags +++ /dev/null @@ -1,308 +0,0 @@ -7e9441311eb21dd1fbc32cfbad58168e46c5450e 0.6 -26f429772565f69d1f6d21adf57c3d8c40197129 0.6.1 -6f46749a7454be6e044a54cd73c51318b74bdee8 0.6.2 -34b80fb58862d18f8f957f98a883ed4a72d06f8e 0.6.3 -fb04abddb50d82a9005c9082c94d5eb983be1d79 0.6.4 -8ae0bd250b4a0d58cbaf16b4354ad60f73f24a01 0.6.5 -88847883dfed39829d3a5ed292ad540723ad31cc 0.6.6 -fcbef325349ada38f6c674eb92db82664cf6437c 0.6.7 -3af7f2b8270b9bb34fb65f08ee567bfe8e2a6a5a 0.6.8 -669725d03fd1e345ea47590e9b14cb19742b96a2 0.6.9 -eff3ca9c2d8d39e24c221816c52a37f964535336 0.6.10 -88710e34b91c98c9348749722cce3acd574d177d 0.6.11 -5ce754773a43ac21f7bd13872f45c75e27b593f8 0.6.12 -de36566d35e51bee7cfc86ffa694795e52f4147c 0.6.13 -e5f3f0ffe9e1a243d49a06f26c79dd160f521483 0.6.14 -dc03a300ec7a89ad773047172d43e52b34e7cd1e 0.6.15 -e620fb4ee8ba17debadb614fb583c6dfac229dea 0.6.16 -21df276275b5a47c6a994927d69ad3d90cf62b5d 0.6.17 -e9264ca4ba8c24239c36a8426a0394f7c7d5dd83 0.6.18 -aed31b1fa47ed1f39e55c75b76bbbdb80775b7f1 0.6.19 -c6e6273587816c3e486ef7739e53c864a0145251 0.6.20 -7afdf4c84a713fe151e6163ab25d45e8727ce653 0.6.21 -105066342777cd1319a95d7ae0271a2ea1ac33fe 0.6.23 -7b5ef4e6c80e82541dffb5a9a130d81550d5a835 0.6.24 -9c014a80f32e532371826ed1dc3236975f37f371 0.6.25 -ff8c4d6c8e5d2093750a58a3d43b76556570007c 0.6.26 -2a5c42ed097a195e398b97261c40cd66c8da8913 0.6.27 -4ed34b38851f90278cfe2bff75784f7e32883725 0.6.28 -acecfa2cfb6fca207dd2f4e025c695def3bb6b40 0.6.29 -e950f50addff150859f5990b9df2a33c691b6354 0.6.30 -06dae3faee2de50ff17b90719df410b2ebc5b71e 0.6.31 -1f4f79258ed5b418f680a55d3006f41aa6a56d2b 0.6.32 -89f57bf1406a5e745470af35446902c21ac9b6f6 0.6.33 -3c8f9fc13862124cf20ef2ff2140254fb272bb94 0.6.34 -7c3f8b9eb7cfa17481c835d5caaa918d337c7a83 0.6.35 -192094c0d1e2e5d2cb5c718f84a36c9de04b314b 0.6.36 -66d4e3b8899166e4c04189ee1831c649b7ff38bf 0.6.37 -398d58aa8bba33778c30ce72055a27d4b425809c 0.6.38 -f457fc2a3ebe609d8ca7a869eb65b7506ecf49ef 0.6.39 -9b2e2aa06e058c63e06c5e42a7f279ddae2dfb7d 0.7b1 -9089a40343981baa593b9bb5953f9088e9507099 0.6.40 -ad107e9b4beea24516ac4e1e854696e586fe279d 0.6.41 -f30167716b659f96c5e0b7ea3d5be2bcff8c0eac 0.6.42 -8951daac6c1bc7b24c7fb054fd369f2c5b88cdb3 0.7b2 -35086ee286732b0f63d2be18d9f26f2734586e2d 0.6.43 -63e4eb2d61204f77f9b557201a0efa187b05a611 0.7b3 -73aa98aee6bbc4a9d19a334a8ac928dece7799c6 0.6.44 -53b4ac9a748aa28893aaca42c41e5e99568667bb 0.7b4 -ddca71ae5ceb9b14512dc60ea83802c10e224cf0 0.6.45 -7f2c08e9ca22023d1499c512fccc1513813b7dc4 0.7 -024dd30ed702135f5328975042566e48cc479d7d 0.7.1 -d04c05f035e3a5636006fc34f4be7e6c77035d17 0.7.2 -d212e48e0cef689acba57ed017289c027660b23c 0.7.3 -74c6c12268059986f9cc0b535399594f1d131201 0.8b1 -85640475dda0621f20e11db0995fa07f51744a98 0.7.4 -b57e5ba934767dd498669b17551678081b3047b5 0.6.46 -dd5bbc116c53d3732d22f983e7ca6d8cfabd3b08 0.7.5 -512744f3f306aea0fdde4cfd600af8b2d6e773e7 0.8b2 -8af9839a76407eebf3610fcd3e7973f1625abaa2 0.8b3 -ee2c967017024197b38e39ced852808265387a4b 0.6.47 -48d3d26cbea68e21c96e51f01092e8fdead5cd60 0.7.6 -5b3c7981a02b4a86af1b10ae16492899b515d485 0.8b4 -cae9127e0534fc46d7ddbc11f68dc88fd9311459 0.6.48 -1506fa538fff01e70424530a32a44e070720cf3c 0.7.7 -5679393794978a1d3e1e087472b8a0fdf3d8423c 0.8b5 -26f59ec0f0f69714d28a891aaad048e3b9fcd6f7 0.8b6 -f657df1f1ed46596d236376649c99a470662b4ba 0.6.49 -236de1de68b14230036147c7c9e7c09b215b53ee 0.7.8 -979d598822bc64b05fb177a2ba221e75ee5b44d3 0.8b7 -e3d70539e79f39a97f69674ab038661961a1eb43 0.8 -3078b1e566399bf0c5590f3528df03d0c23a0777 0.9 -9e5a8f734662dd36e6fd6e4ba9031d0e2d294632 0.9.1 -37444bb32e172aaacbc0aeafdf5a778ee471723d 0.9.2 -3e9d2e89de3aa499382d6be2ec8b64d2a29f7f13 0.9.3 -1aef141fc968113e4c521d1edf6ea863c4ff7e00 0.9.4 -88e3d6788facbb2dd6467a23c4f35529a5ce20a1 0.9.5 -acc6c5d61d0f82040c237ac7ea010c0fc9e67d66 0.9.6 -19965a03c1d5231c894e0fabfaf45af1fd99f484 0.9.7 -e0a6e225ad6b28471cd42cfede6e8a334bb548fb 0.9.8 -7b91ff93a30ef78634b7bb34f4a6229a5de281ee 1.0b1 -aba16323ec9382da7bc77c633990ccb3bd58d050 1.0b2 -8a98492f0d852402c93ddbbf3f07081909a9105f 1.0b3 -c385fdf1f976fb1d2a6accc9292d8eca419180fa 1.0 -d943b67fe80dbd61326014e4acedfc488adfa1c9 1.1 -2e42e86546100c9f6845b04eb31b75c5add05f78 1.1.1 -462fe5ccd8befeb2a235e8295d6d73eb3a49cc78 1.1.2 -ddf3561d6a54087745f4bf6ea2048b86195d6fe2 1.1.3 -f94c7e4fa03077e069c1c3cef93ead735559e706 1.1.4 -d9bb58331007ee3f69d31983a180f56b15c731c3 1.1.5 -5e426bdeb46b87e299422adc419f4163b6c78d13 1.1.6 -cc9b19cd0ec64e44308a852e9b9fdc6026ea2e46 1.1.7 -4c7dc4ae2440ae3e9ba26b4a12ffca3407e7030d 1.2b1 -77921bbe3931caf40474dc36e55d3d541981c749 1.2 -19873119647deae8a68e9ed683317b9ee170a8d8 1.3 -a197b626075a8c2e393a08c42a20bd2624a41092 1.3.1 -076b472a9e3f840021e9d5509878337e6e5fcd89 1.3.2 -0d1bdb99a535a2c7ed4edd37141fb0b54348b713 1.4b1 -a13f8c18ce742bc83c794b9eea57980cb94ae18a 1.4 -9a5f26d7df8ef779cb5f40cc0389343fb4c61365 1.4.1 -274cb3beba4f22d5f461b0578b6d56e171d94f2e 1.4.2 -0bb1df93c2eaa50e95ccfce18208b0cca20ebae3 2.0 -bbdba51e1bc1779728ed351529252f73543ace65 2.0.1 -5a62ac60ba31d249db1cfcff31d85ca26421be6d 2.0.2 -c49c651997ebec3b40b71139e8a6a6a15c62c848 2.1 -b5be6c2b828cb92d27f52fccc725ce86a37e9ce0 2.1.1 -ab1c2a26e06f2a2006e8e867e4d41ccf1d6cf9b2 2.2b1 -caab085e829f29679d0e47430b2761af6b20fc76 2.1.2 -39f7ef5ef22183f3eba9e05a46068e1d9fd877b0 2.2 -faba785e9b9e05ba890d0851ef1f3287c32fcac2 3.0b1 -8e8c50925f18eafb7e66fe020aa91a85b9a4b122 3.0 -cd9e857476ac70515f7436f846b593f696ac672d 3.0.1 -bad1f30ee0dfa7a2af4f428d06f62efa39ca48db 3.0.2 -47224d55ddc6bb08c1d17a219f124d0d9c524491 3.1 -07c459bea1c58ff52e0576fc29c1865d18a83b09 3.2 -b306e681a945406833fb297ae10241e2241fc22b 3.3 -78c8cfbe3e1017d1653c48f7306b2c4b4911bf1a 4.0b1 -5cb90066d98700e6d37a01d95c4a2090e730ae02 3.4 -e39de2d3eb774b70c023a1151758213cc9ed2178 3.4.1 -369f6f90f69683702cc0b72827ccf949977808b0 3.4.2 -06a56e063c327b0606f9e9690764279d424646b2 3.4.3 -0917d575d26091a184796624743825914994bf95 3.4.4 -98f29d521c3a57bae0090d2bc5597d93db95b108 3.5 -254d8c625f4620993ce2d2b21212ba01cf307fe6 3.5.1 -572201d08eadc59210f6f0f28f9dc79f906672d3 3.5.2 -e94e768594a1405efde0b79cc60549dd8a4cda9a 3.6 -292dfca15d33e72a862d044183a6ad7c06862a19 3.7b1 -49bd27eebf212c067392796bb2d0fa6d8e583586 3.7 -2fa97c06cc013a9c82f4c1219711e72238d5b6e6 3.8 -9b422fc0b8b97cdb62f02d754283f747adef7f83 3.7.1 -40744de29b848f0e88139ba91d645c08a56855e9 3.8.1 -84d936fd18a93d16c46e68ee2e39f5733f3cd863 5.0 -871bd7b4326f48860ebe0baccdaea8fe4f8f8583 5.0.1 -95996b713722376679c3168b15ab12ea8360dd5f 5.0.2 -3a948b6d01e3449b478fcdc532c44eb3cea5ee10 5.1 -f493e6c4ffd88951871110858c141385305e0077 5.2 -1f9505cfd7524ce0c83ab31d139f47b39c56ccbe 5.3 -baae103e80c307008b156e426a07eb9f486eb4f0 5.4 -ba3b08c7bffd6123e1a7d58994f15e8051a67cb7 5.4.1 -7adcf1397f6eccb9e73eda294343de2943f7c8fb 5.4.2 -68910a89f97a508a64f9f235dc64ad43d4477ea0 5.5 -949a66af4f03521e1404deda940aa951418a13d2 5.5.1 -a1fc0220bfa3581158688789f6dfdc00672eb99b 5.6 -37ed55fd310d0cd32009dc5676121e86b404a23d 5.7 -67550a8ed9f4ef49ee5a31f433adbf5a0eaeccf9 5.8 -755cbfd3743ffb186cdf7e20be8e61dbdaa22503 6.0 -bc6655b4acf205dd9f25c702955645656077398a 6.0.1 -1ae2a75724bbba56373784f185a7f235ed0f24a4 6.0.2b1 -01271e84e5125fcc4f0f368a6e21116a5722953c 6.0.2 -7ea80190d494a766c6356fce85c844703964b6cc 6.1 -df26609c2f614f5fc9110342e4003ee8bd95cf84 7.0 -850a5c155c48b6ecfbb83b961586ea359b561522 8.0b1 -7ea0e7498e4ddbf63b6929ee83c75a9207996b08 8.0 -1af3a5f24f7dd4e51d117f701918052b7de65c99 8.1b1 -d62bf4e407b3b9b5bedcc1396a9ba46f35571902 8.0.1 -1c03d512e39d5cfd711ae3ed7e316769f427e43b 8.0.2 -6c3467488123ce70b1dd009145a02f51fb78cdcc 8.0.3 -2c467afffe9fe1e14618b576fac6b4f7c412a61e 8.0.4 -3f87370b6863e5a4e831b394ef1a58e0e97a4336 8.1 -995f6d9651312cd481ca1e5ddb271cbdd0474c57 8.2 -efbe39dae0aba9a7db399f6442758ae94e315c93 8.2.1 -cd14b2a72e51c7d13873ab6c2041f901b1a7a1cd 8.3 -0eee586a153f068142c1a0df4bc2635ed2c1a1cc 9.0b1 -921e60a0f9067311571fde9ccf2f35223159d9f6 8.4 -0d7b9b63d06ab7f68bc8edd56cb2034e6395d7fc 9.0 -fa069bf2411a150c9379d31a04d1c3836e2d3027 9.0.1 -3ed27d68d3f41bb5daa2afecfa9180d5958fe9d3 9.1 -0c4d18a747a6d39bff8e194a58af949a960d674a 10.0 -4c41e2cdd70beb0da556d71f46a67734c14f2bc2 10.0.1 -26b00011ec65b8f7b4f3d51078ec0a694701a45c 10.1 -651d41db58849d4fc50e466f4dc458d448480c4e 10.2 -1f5de53c079d577ead9d80265c9e006503b16457 10.2.1 -b4b92805bc0e9802da0b597d00df4fa42b30bc40 11.0 -6cd2b18f4be2a9c188fa505b34505b32f4a4554b 11.1 -feb5971e7827483bbdeb67613126bb79ed09e6d9 11.2 -a1a6a1ac9113b90009052ca7263174a488434099 11.3 -1116e568f534ad8f4f41328a0f5fa183eb739c90 11.3.1 -55666947c9eb7e3ba78081ad6ae004807c84aede 12.0 -747018b2e35a40cb4b1c444f150f013d02197c64 12.0.1 -a177ea34bf81662b904fe3af46f3c8719a947ef1 12.0.2 -bf8c5bcacd49bf0f9648013a40ebfc8f7c727f7b 12.0.3 -73dcfc90e3eecec6baddea19302c6b342e68e2fa 12.0.4 -01fbfc9194a2bc502edd682eebbf4d2f1bc79eee 12.0.5 -7bca8938434839dbb546b8bfccd9aab7a86d851e 12.1 -5ff5c804a8fa580cff499ba0025ff2e6a5474fd0 12.2 -8d50aac3b20793954121edb300b477cc75f3ec96 12.3 -297931cb8cac7d44d970adb927efd6cb36ac3526 12.4 -df34cc18624279faffdbc729c0a11e6ab0f46572 13.0 -ae1a5c5cf78f4f9f98c054f1c8cec6168d1d19b4 13.0.1 -e22a1d613bddf311e125eecd9c1e1cad02ab5063 13.0.2 -a3a105f795f8362f26e84e9acbc237ee2d6bcca4 14.0 -9751a1671a124e30ae344d1510b9c1dbb14f2775 14.1 -07fcc3226782b979cedaaf456c7f1c5b2fdafd2c 14.1.1 -d714fb731de779a1337d2d78cd413931f1f06193 14.2 -e3c635a7d463c7713c647d1aa560f83fd8e27ef0 14.3 -608948cef7e0ab8951691b149f5b6f0184a5635e 14.3.1 -617699fd3e44e54b6f95b80bfcf78164df37f266 15.0b1 -d2c4d84867154243993876d6248aafec1fd12679 15.0 -10fde952613b7a3f650fb1f6b6ed58cbd232fa3c 15.1 -df5dc9c7aa7521f552824dee1ed1315cfe180844 15.2 -e0825f0c7d5963c498266fe3c175220c695ae83b 16.0 -8e56240961015347fed477f00ca6a0783e81d3a2 17.0 -a37bcaaeab367f2364ed8c070659d52a4c0ae38e 17.1 -4a0d01d690ff184904293e7a3244ac24ec060a73 17.1.1 -fac98a49bd984ef5accf7177674d693277bfbaef 18.0b1 -0a49ee524b0a1d67d2a11c8c22f082b57acd7ae1 18.0 -e364795c1b09c70b6abb53770e09763b52bf807d 18.0.1 -c0395f556c35d8311fdfe2bda6846b91149819cd 18.1 -1a981f2e5031f55267dc2a28fa1b42274a1b64b2 18.2 -b59320212c8371d0be9e5e6c5f7eec392124c009 18.3 -7a705b610abb1177ca169311c4ee261f3e4f0957 18.3.1 -1e120f04bcaa2421c4df0eb6678c3019ba4a82f6 18.3.2 -6203335278be7543d31790d9fba55739469a4c6c 18.4 -31dc6d2ac0f5ab766652602fe6ca716fff7180e7 18.5 -dfe190b09908f6b953209d13573063809de451b8 18.6 -804f87045a901f1dc121cf9149143d654228dc13 18.6.1 -67d07805606aead09349d5b91d7d26c68ddad2fc 18.7 -3041e1fc409be90e885968b90faba405420fc161 18.7.1 -c811801ffa1de758cf01fbf6a86e4c04ff0c0935 18.8 -fbf06fa35f93a43f044b1645a7e4ff470edb462c 18.8.1 -cc41477ecf92f221c113736fac2830bf8079d40c 19.0 -834782ce49154e9744e499e00eb392c347f9e034 19.1 -0a2a3d89416e1642cf6f41d22dbc07b3d3c15a4d 19.1.1 -5d24cf9d1ced76c406ab3c4a94c25d1fe79b94bc 19.2 -66fa131a0d77a1b0e6f89ccb76b254cfb07d3da3 19.3b1 -32bba9bf8cce8350b560a7591c9ef5884a194211 19.3 -f47f3671508b015e9bb735603d3a0a6ec6a77b01 19.4 -0bda3291ac725750b899b4ba3e4b6765e7645daa 19.4.1 -0a68cbab72580a6f8d3bf9c45206669eefcd256b 19.5 -34121bf49b1a7ac77da7f7c75105c8a920218dd7 19.6b1 -3c2332e4ec72717bf17321473e5c3ad6e5778903 19.6 -35d9179d04390aada66eceae9ceb7b9274f67646 19.6.1 -d2782cbb2f15ca6831ab9426fbf8d4d6ca60db8a 19.6.2 -c6e619ce910d1650cc2433f94e5594964085f973 19.7 -2a60daeff0cdb039b20b2058aaad7dae7bcd2c1c 20.0 -06c9d3ffae80d7f5786c0a454d040d253d47fc03 20.1 -919a40f1843131249f98104c73f3aee3fc835e67 20.1.1 -74c4ffbe1f399345eb4f6a64785cfff54f7e6e7e 20.2 -1aacb05fbdfe06cee904e7a138a4aa6df7b88a63 20.2.1 -48aa5271ef1cd5379cf91a1c958e490692b978e7 20.2.2 -9c55a3a1268a33b4a57b96b2b9fa2cd0701780ee 20.3 -3e87e975a95c780eec497ef9e5a742f7adfb77ec 20.3.1 -06692c64fb9b5843331a918ab7093f151412ec8e 20.4 -f8174392e9e9c6a21ea5df0f22cb4ca885c799ca 20.5 -114f3dbc8a73dacbce2ebe08bb70ca76ab18390e v20.6.0 -a3d4006688fe5e754d0e709a52a00b8191819979 v20.6.1 -2831509712601a78fddf46e51d6f41ae0f92bd0e v20.6.2 -8b46dc41cb234c435b950a879214a6dee54c9dd2 v20.6.3 -7258be20fe93bbf936dc1a81ce71c04c5880663e v20.6.4 -7e0ab283db4e6f780777f7f06af475f044631fa1 v20.6.5 -57d63b38e85515d06e06d3cea62e35e6c54b5093 v20.6.6 -57d63b38e85515d06e06d3cea62e35e6c54b5093 v20.6.6 -b04dbdd161d7f68903a53e1dbd1fa5b5fde73f94 v20.6.6 -0804d30b6ead64e0e324aefd67439b84df2d1c01 v20.6.7 -a00910db03ec15865e4c8506820d4ad1df3e26f3 v20.6.8 -0262ab29fc2417b502a55f49b7fd43528fbd3df4 v20.7.0 -7f56b6f40de39456c78507a14c288709712881cb v20.8.0 -8cf9340669ae26e2b31f68b9c3f885ab7bdd65ce v20.8.1 -8bf8aaa139bb6a36fcd243214d6730a214ae08f5 v20.9.0 -c72faa468919fd2f226c97e94d4e64a6506860e5 v20.10.0 -3b5fdd077c7d83d02c4979ad69cc0bf199b47587 v20.10.1 -ddd3f81eb9e0860bf95c380c50a72c52a215231f v21.0.0 -018e4a727cf691d6404cd24ffb25e8eebea2fad4 v20.6.8 -02643fe9503033edd2fc5a54c8d4361a6c185be4 v21.1.0 -40b8fac6db119aca9c462993d01908492769fc4f v21.2.0 -40b8fac6db119aca9c462993d01908492769fc4f v21.2.0 -9959424676a4aac1c14e430ff6f4210fdb0442d9 v21.2.0 -694111eadb10fe6003078895a2cbb803ce514ef2 v21.2.1 -274f33435e9c3ba5019f2a2bfe478fa2db0da41d v21.2.2 -451fbedb4c226d8ea5b6eab1e21679c9a4ec4a93 v22.0.0 -f5c4923b0400d61f67699c2d54388878f9e0c8bd v22.0.1 -8610a8b9635f15d33f94fccb295fd34aa6fbddee v22.0.2 -efee7d74a8478c0d08c801fb520e41b6e04d0dda v22.0.3 -77b20c09b04775cc936ab5d16cbc46ff05fc7080 v22.0.4 -d5832e5deb77027da474e79e5f047e9a81f7edf8 v22.0.5 -8664c631bf3a817a7deba86c13b67eccc1f81091 v23.0.0 -6c74559c732c56f61b465d613458ec1a930884b6 v23.1.0 -65b3fe899db4086e66afa067a1311eea2a88d5e2 v23.2.0 -e10c848a82ffb925741c65dd8a8fc8b50b3c3e14 v23.2.1 -a011298221c3d47aa539ae4c119c51861caf6438 v23.2.1 -8d37b17a93ec3e5fff9e040fc3f14ab7b7b24b2c v24.0.0 -130a58f9503fe07ca8c7a34675b7d3a976f163d7 v24.0.1 -7996c56bf6a2f81427b2f91eb11e64d690353493 v24.0.2 -d425bd1ee620772fe90e0dd2a7530b0d6a642601 v24.0.3 -a7d2f79f0996d881794af0f87595032098202811 v24.1.0 -d29075e7f8797891e8c59fb58c4d8d1b79954b34 v24.1.1 -ed9e7bd8caf95261d528ee3db117611dc42814eb v24.2.0 -5b577d179a7e2f3020712c376c0200901e5c93c1 v24.2.1 -83ca05973c16102145b339aec7e170d94966a2ba v24.3.0 -e14229dd2abc034530447d64ed87fddb944347bd v24.3.1 -58e92028ab0061f1f80d98e769c9143305275242 v25.0.0 -0591bce16c7f94191cea925929cc8b0ce6baca09 v25.0.1 -dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 -dc92db3e29a4d1ac57d383091e6cf734d04ed54b v25.0.2 -91eeaf2f33db99d8f78f8261931a1aea8fe8d952 v25.0.2 -529a76a860c50d3cc262759b5b9ce28f171236f9 v25.1.0 -392ee093902e14a1d2a6eefc389a7b9ac78b3f9e v25.1.1 -1cbb29c235439331a76c7b6b5cf8701f763478d3 v25.1.2 -c350190e7bbf274e6728f14af7451b1fd3aaeba2 v25.1.2 -86e668badaf45315bb8506ac2312665d129a0322 v25.1.3 -6f55250b9c5856557ac669d1f966bba8be9eb1d2 v25.1.4 -76143bb477b50314ab6f4ccc4ced80ee43f0dc94 v25.1.5 -2db4c66aeae47217aaf92099a9875e9e810c9cbb v25.1.6 -2083d7c3fadcf15b3bc07f7532440efbcf8fd18d v25.2.0 -2371456ae99d11187c33deacf1308aded31081d9 v25.3.0 -f713f9faaaa33c0e9a628dc9322ef8d1fbeb8319 v25.4.0 -7cb13a0cd176f39500701b24dbfec603ead5110c v26.0.0 -b299cfc9d7d89070e8eec9751a8be72c8a75506b v26.1.0 -e1d057e23b2fec5991084744c356a6c7e05b219d v26.1.1 -- cgit v1.2.1 From 69061481e345bfd1f1d07795b3541c07e498d2df Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 14:13:03 -0500 Subject: Add changelog entry. Ref #719. --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index eda52476..b69ac26c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,9 @@ CHANGES ======= +* #719: Suppress decoding errors and instead log a warning + when metadata cannot be decoded. + v25.1.4 ------- -- cgit v1.2.1 From 35ea365b50bd1a64375fdbcce187affab22af3b7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Sep 2016 14:17:34 -0500 Subject: Put main logic in the top-level body of the function. --- pkg_resources/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index af986ac3..37bf1482 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1859,12 +1859,13 @@ class FileMetadata(EmptyProvider): return name == 'PKG-INFO' and os.path.isfile(self.path) def get_metadata(self, name): - if name == 'PKG-INFO': - with io.open(self.path, encoding='utf-8', errors="replace") as f: - metadata = f.read() - self._warn_on_replacement(metadata) - return metadata - raise KeyError("No metadata except PKG-INFO is available") + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata def _warn_on_replacement(self, metadata): # Python 2.6 and 3.2 compat for: replacement_char = '�' -- cgit v1.2.1