Commit 2fbcd342 authored by J. Goutin's avatar J. Goutin

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.
parent 127f30f8
...@@ -379,12 +379,33 @@ class RegistryInfo: ...@@ -379,12 +379,33 @@ class RegistryInfo:
path = r'DevDiv\VCForPython' path = r'DevDiv\VCForPython'
return os.path.join(self.microsoft, path) 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 @property
def windows_sdk(self): def windows_sdk(self):
""" """
Microsoft Windows/Platform SDK registry key. 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): def lookup(self, key, name):
""" """
...@@ -429,6 +450,7 @@ class SystemInfo: ...@@ -429,6 +450,7 @@ class SystemInfo:
def __init__(self, registry_info, vcver=None): def __init__(self, registry_info, vcver=None):
self.ri = registry_info self.ri = registry_info
self.pi = self.ri.pi
if vcver: if vcver:
self.vcver = vcver self.vcver = vcver
else: else:
...@@ -503,22 +525,29 @@ class SystemInfo: ...@@ -503,22 +525,29 @@ class SystemInfo:
return result return result
@property @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: if self.vcver <= 9.0:
sdkver = ('7.0', '6.1', '6.0a') return ('7.0', '6.1', '6.0a')
elif self.vcver == 10.0: elif self.vcver == 10.0:
sdkver = ('7.1', '7.0a') return ('7.1', '7.0a')
elif self.vcver == 11.0: elif self.vcver == 11.0:
sdkver = ('8.0', '8.0a') return ('8.0', '8.0a')
elif self.vcver == 12.0: elif self.vcver == 12.0:
sdkver = ('8.1', '8.1a') return ('8.1', '8.1a')
elif self.vcver >= 14.0: elif self.vcver >= 14.0:
sdkver = ('10.0', '8.1') return ('10.0', '8.1')
for ver in sdkver:
@property
def WindowsSdkDir(self):
"""
Microsoft Windows SDK directory.
"""
sdkdir = ''
for ver in self.WindowsSdkVersion:
# Try to get it from registry # Try to get it from registry
loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver) loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver)
sdkdir = self.ri.lookup(loc, 'installationfolder') sdkdir = self.ri.lookup(loc, 'installationfolder')
...@@ -532,7 +561,7 @@ class SystemInfo: ...@@ -532,7 +561,7 @@ class SystemInfo:
sdkdir = os.path.join(install_base, 'WinSDK') sdkdir = os.path.join(install_base, 'WinSDK')
if not sdkdir or not os.path.isdir(sdkdir): if not sdkdir or not os.path.isdir(sdkdir):
# If fail, use default new path # If fail, use default new path
for ver in sdkver: for ver in self.WindowsSdkVersion:
intver = ver[:ver.rfind('.')] intver = ver[:ver.rfind('.')]
path = r'Microsoft SDKs\Windows Kits\%s' % (intver) path = r'Microsoft SDKs\Windows Kits\%s' % (intver)
d = os.path.join(self.ProgramFiles, path) d = os.path.join(self.ProgramFiles, path)
...@@ -540,7 +569,7 @@ class SystemInfo: ...@@ -540,7 +569,7 @@ class SystemInfo:
sdkdir = d sdkdir = d
if not sdkdir or not os.path.isdir(sdkdir): if not sdkdir or not os.path.isdir(sdkdir):
# If fail, use default old path # If fail, use default old path
for ver in sdkver: for ver in self.WindowsSdkVersion:
path = r'Microsoft SDKs\Windows\v%s' % ver path = r'Microsoft SDKs\Windows\v%s' % ver
d = os.path.join(self.ProgramFiles, path) d = os.path.join(self.ProgramFiles, path)
if os.path.isdir(d): if os.path.isdir(d):
...@@ -550,6 +579,37 @@ class SystemInfo: ...@@ -550,6 +579,37 @@ class SystemInfo:
sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK') sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
return sdkdir 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 @property
def FSharpInstallDir(self): def FSharpInstallDir(self):
""" """
...@@ -559,6 +619,48 @@ class SystemInfo: ...@@ -559,6 +619,48 @@ class SystemInfo:
path = os.path.join(self.ri.visualstudio, path) path = os.path.join(self.ri.visualstudio, path)
return self.ri.lookup(path, 'productdir') or '' 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 @property
def FrameworkDir32(self): def FrameworkDir32(self):
""" """
...@@ -611,10 +713,8 @@ class SystemInfo: ...@@ -611,10 +713,8 @@ class SystemInfo:
if self.vcver >= 12.0: if self.vcver >= 12.0:
frameworkver = (ver, 'v4.0') frameworkver = (ver, 'v4.0')
elif self.vcver >= 10.0: elif self.vcver >= 10.0:
if ver.lower()[:2] != 'v4': frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver,
ver = '' 'v3.5')
ver = ver or 'v4.0.30319'
frameworkver = (ver, 'v3.5')
elif self.vcver == 9.0: elif self.vcver == 9.0:
frameworkver = ('v3.5', 'v2.0.50727') frameworkver = ('v3.5', 'v2.0.50727')
if self.vcver == 8.0: if self.vcver == 8.0:
...@@ -647,9 +747,10 @@ class EnvironmentInfo: ...@@ -647,9 +747,10 @@ class EnvironmentInfo:
self.ri = RegistryInfo(self.pi) self.ri = RegistryInfo(self.pi)
self.si = SystemInfo(self.ri, vcver) self.si = SystemInfo(self.ri, vcver)
if self.vcver < vcvermin: if vcvermin:
err = 'No suitable Microsoft Visual C++ version found' if self.vcver < vcvermin:
raise distutils.errors.DistutilsPlatformError(err) err = 'No suitable Microsoft Visual C++ version found'
raise distutils.errors.DistutilsPlatformError(err)
@property @property
def vcver(self): def vcver(self):
...@@ -664,11 +765,13 @@ class EnvironmentInfo: ...@@ -664,11 +765,13 @@ class EnvironmentInfo:
Microsoft Visual Studio Tools Microsoft Visual Studio Tools
""" """
paths = [r'Common7\IDE', r'Common7\Tools'] paths = [r'Common7\IDE', r'Common7\Tools']
if self.vcver >= 14.0: if self.vcver >= 14.0:
arch_subdir = self.pi.current_dir(hidex86=True, x64=True) arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
paths += [r'Team Tools\Performance Tools'] paths += [r'Team Tools\Performance Tools']
paths += [r'Team Tools\Performance Tools%s' % arch_subdir] paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
return [os.path.join(self.si.VSInstallDir, path) for path in paths] return [os.path.join(self.si.VSInstallDir, path) for path in paths]
@property @property
...@@ -686,8 +789,10 @@ class EnvironmentInfo: ...@@ -686,8 +789,10 @@ class EnvironmentInfo:
""" """
arch_subdir = self.pi.target_dir(hidex86=True) arch_subdir = self.pi.target_dir(hidex86=True)
paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
if self.vcver >= 14.0: if self.vcver >= 14.0:
paths += [r'Lib\store%s' % arch_subdir] paths += [r'Lib\store%s' % arch_subdir]
return [os.path.join(self.si.VCInstallDir, path) for path in paths] return [os.path.join(self.si.VCInstallDir, path) for path in paths]
@property @property
...@@ -706,15 +811,16 @@ class EnvironmentInfo: ...@@ -706,15 +811,16 @@ class EnvironmentInfo:
""" """
forcex86 = True if self.vcver <= 10.0 else False forcex86 = True if self.vcver <= 10.0 else False
arch_subdir = self.pi.cross_dir(forcex86) arch_subdir = self.pi.cross_dir(forcex86)
tools = [ tools = [os.path.join(self.si.VCInstallDir, 'VCPackages'),
os.path.join(self.si.VCInstallDir, 'VCPackages'), os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir)]
os.path.join(self.si.VCInstallDir, 'Bin%s' % arch_subdir),
]
if self.pi.cross_dir() and self.vcver >= 14.0: if self.pi.cross_dir() and self.vcver >= 14.0:
path = 'Bin%s' % self.pi.current_dir(hidex86=True) path = 'Bin%s' % self.pi.current_dir(hidex86=True)
tools += [os.path.join(self.si.VCInstallDir, path)] tools += [os.path.join(self.si.VCInstallDir, path)]
else: else:
tools += [os.path.join(self.si.VCInstallDir, 'Bin')] tools += [os.path.join(self.si.VCInstallDir, 'Bin')]
return tools return tools
@property @property
...@@ -722,39 +828,71 @@ class EnvironmentInfo: ...@@ -722,39 +828,71 @@ class EnvironmentInfo:
""" """
Microsoft Windows SDK Libraries Microsoft Windows SDK Libraries
""" """
arch_subdir = self.pi.target_dir(hidex86=True, x64=True) if self.vcver <= 10.0:
return [os.path.join(self.si.WindowsSdkDir, 'Bin%s' % arch_subdir)] 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 @property
def OSIncludes(self): def OSIncludes(self):
""" """
Microsoft Windows SDK Include Microsoft Windows SDK Include
""" """
include = os.path.join(self.si.WindowsSdkDir, 'include')
if self.vcver <= 10.0: if self.vcver <= 10.0:
return [ return [include, os.path.join(include, 'gl')]
os.path.join(self.si.WindowsSdkDir, 'Include'),
os.path.join(self.si.WindowsSdkDir, r'Include\gl') else:
] if self.vcver >= 14.0:
elif self.vcver <= 12.0: sdkver = self._get_content_dirname(include)
return [ else:
os.path.join(self.si.WindowsSdkDir, r'include\shared'), sdkver = ''
os.path.join(self.si.WindowsSdkDir, r'include\um'), return [os.path.join(include, '%sshared' % sdkver),
os.path.join(self.si.WindowsSdkDir, r'include\winrt') 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 @property
def SdkTools(self): def SdkTools(self):
""" """
Microsoft Windows SDK Tools Microsoft Windows SDK Tools
""" """
if self.vcver <= 11.0: tools = [os.path.join(self.si.WindowsSdkDir,
tools = [os.path.join(self.si.WindowsSdkDir, 'Bin')] 'Bin' if self.vcver <= 11.0 else r'Bin\x86')]
else:
tools = [os.path.join(self.si.WindowsSdkDir, r'Bin\x86')]
if not self.pi.current_is_x86(): if not self.pi.current_is_x86():
arch_subdir = self.pi.current_dir(x64=True) arch_subdir = self.pi.current_dir(x64=True)
path = 'Bin%s' % arch_subdir path = 'Bin%s' % arch_subdir
tools += [os.path.join(self.si.WindowsSdkDir, path)] tools += [os.path.join(self.si.WindowsSdkDir, path)]
if self.vcver == 10.0 or self.vcver == 11.0: if self.vcver == 10.0 or self.vcver == 11.0:
if self.pi.target_is_x86(): if self.pi.target_is_x86():
arch_subdir = '' arch_subdir = ''
...@@ -762,6 +900,10 @@ class EnvironmentInfo: ...@@ -762,6 +900,10 @@ class EnvironmentInfo:
arch_subdir = self.pi.current_dir(hidex86=True, x64=True) arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
tools += [os.path.join(self.si.WindowsSdkDir, path)] tools += [os.path.join(self.si.WindowsSdkDir, path)]
if self.si.WindowsSDKExecutablePath:
tools += [self.si.WindowsSDKExecutablePath]
return tools return tools
@property @property
...@@ -778,23 +920,21 @@ class EnvironmentInfo: ...@@ -778,23 +920,21 @@ class EnvironmentInfo:
""" """
pi = self.pi pi = self.pi
si = self.si si = self.si
if self.vcver <= 10.0: if self.vcver <= 10.0:
include32 = True include32 = True
include64 = not pi.target_is_x86() and not pi.current_is_x86() include64 = not pi.target_is_x86() and not pi.current_is_x86()
else: else:
include32 = pi.target_is_x86() or pi.current_is_x86() include32 = pi.target_is_x86() or pi.current_is_x86()
include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
tools = [] tools = []
if include32: if include32:
tools += [ tools += [os.path.join(si.FrameworkDir32, ver)
os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32]
for ver in si.FrameworkVersion32
]
if include64: if include64:
tools += [ tools += [os.path.join(si.FrameworkDir64, ver)
os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64]
for ver in si.FrameworkVersion64
]
return tools return tools
@property @property
...@@ -802,17 +942,22 @@ class EnvironmentInfo: ...@@ -802,17 +942,22 @@ class EnvironmentInfo:
""" """
Microsoft .Net Framework SDK Libraries Microsoft .Net Framework SDK Libraries
""" """
if self.vcver < 14.0: if self.vcver < 14.0 or not self.si.NetFxSdkDir:
return [] return []
arch_subdir = self.pi.target_dir(x64=True)
return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
@property @property
def NetFxSDKIncludes(self): def NetFxSDKIncludes(self):
""" """
Microsoft .Net Framework SDK Includes Microsoft .Net Framework SDK Includes
""" """
if self.vcver < 14.0: if self.vcver < 14.0 or not self.si.NetFxSdkDir:
return [] return []
return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
@property @property
def VsTDb(self): def VsTDb(self):
""" """
...@@ -827,24 +972,22 @@ class EnvironmentInfo: ...@@ -827,24 +972,22 @@ class EnvironmentInfo:
""" """
if self.vcver < 12.0: if self.vcver < 12.0:
return [] return []
arch_subdir = self.pi.current_dir(hidex86=True) 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.vcver, arch_subdir)
return [ return [os.path.join(self.si.ProgramFilesx86, path),
os.path.join(self.si.ProgramFilesx86, path), os.path.join(self.si.ProgramFiles, path)]
os.path.join(self.si.ProgramFiles, path)
]
@property @property
def HTMLWs(self): def HTMLHelpWorkshop(self):
""" """
Microsoft HTML Help Workshop Microsoft HTML Help Workshop
""" """
if self.vcver < 11.0: if self.vcver < 11.0:
return [] return []
return [
os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'), return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop'),
os.path.join(self.si.ProgramFiles, 'HTML Help Workshop') os.path.join(self.si.ProgramFiles, 'HTML Help Workshop')]
]
@property @property
def UCRTLibraries(self): def UCRTLibraries(self):
...@@ -854,6 +997,11 @@ class EnvironmentInfo: ...@@ -854,6 +997,11 @@ class EnvironmentInfo:
if self.vcver < 14.0: if self.vcver < 14.0:
return [] 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 @property
def UCRTIncludes(self): def UCRTIncludes(self):
""" """
...@@ -862,6 +1010,10 @@ class EnvironmentInfo: ...@@ -862,6 +1010,10 @@ class EnvironmentInfo:
if self.vcver < 14.0: if self.vcver < 14.0:
return [] return []
include = os.path.join(self.si.UniversalCRTSdkDir, 'include')
ucrtver = self._get_content_dirname(include)
return [os.path.join(include, '%sucrt' % ucrtver)]
@property @property
def FSharp(self): def FSharp(self):
""" """
...@@ -869,6 +1021,7 @@ class EnvironmentInfo: ...@@ -869,6 +1021,7 @@ class EnvironmentInfo:
""" """
if self.vcver < 11.0 and self.vcver > 12.0: if self.vcver < 11.0 and self.vcver > 12.0:
return [] return []
return self.si.FSharpInstallDir return self.si.FSharpInstallDir
@property @property
...@@ -900,7 +1053,8 @@ class EnvironmentInfo: ...@@ -900,7 +1053,8 @@ class EnvironmentInfo:
libpath=self._build_paths('libpath', libpath=self._build_paths('libpath',
[self.VCLibraries, [self.VCLibraries,
self.FxTools, self.FxTools,
self.VCStoreRefs]), self.VCStoreRefs,
self.OSLibpath]),
path=self._build_paths('path', path=self._build_paths('path',
[self.VCTools, [self.VCTools,
self.VSTools, self.VSTools,
...@@ -909,7 +1063,7 @@ class EnvironmentInfo: ...@@ -909,7 +1063,7 @@ class EnvironmentInfo:
self.SdkSetup, self.SdkSetup,
self.FxTools, self.FxTools,
self.MSBuild, self.MSBuild,
self.HTMLWs, self.HTMLHelpWorkshop,
self.FSharp]), self.FSharp]),
) )
if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist): if self.vcver >= 14 and os.path.isfile(self.VCRuntimeRedist):
...@@ -940,9 +1094,11 @@ class EnvironmentInfo: ...@@ -940,9 +1094,11 @@ class EnvironmentInfo:
""" """
List unique elements, preserving order. List unique elements, preserving order.
Remember all elements ever seen. 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 = set()
seen_add = seen.add seen_add = seen.add
filterfalse = six.moves.filterfalse filterfalse = six.moves.filterfalse
...@@ -956,3 +1112,24 @@ class EnvironmentInfo: ...@@ -956,3 +1112,24 @@ class EnvironmentInfo:
if k not in seen: if k not in seen:
seen_add(k) seen_add(k)
yield element 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 ''
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment