4268
4270
% (os.path.basename(dirname), printable_e))
4271
class Feature(object):
4272
"""An operating system Feature."""
4275
self._available = None
4277
def available(self):
4278
"""Is the feature available?
4280
:return: True if the feature is available.
4282
if self._available is None:
4283
self._available = self._probe()
4284
return self._available
4287
"""Implement this method in concrete features.
4289
:return: True if the feature is available.
4291
raise NotImplementedError
4294
if getattr(self, 'feature_name', None):
4295
return self.feature_name()
4296
return self.__class__.__name__
4299
class _SymlinkFeature(Feature):
4302
return osutils.has_symlinks()
4304
def feature_name(self):
4307
SymlinkFeature = _SymlinkFeature()
4310
class _HardlinkFeature(Feature):
4313
return osutils.has_hardlinks()
4315
def feature_name(self):
4318
HardlinkFeature = _HardlinkFeature()
4321
class _OsFifoFeature(Feature):
4324
return getattr(os, 'mkfifo', None)
4326
def feature_name(self):
4327
return 'filesystem fifos'
4329
OsFifoFeature = _OsFifoFeature()
4332
class _UnicodeFilenameFeature(Feature):
4333
"""Does the filesystem support Unicode filenames?"""
4337
# Check for character combinations unlikely to be covered by any
4338
# single non-unicode encoding. We use the characters
4339
# - greek small letter alpha (U+03B1) and
4340
# - braille pattern dots-123456 (U+283F).
4341
os.stat(u'\u03b1\u283f')
4342
except UnicodeEncodeError:
4344
except (IOError, OSError):
4345
# The filesystem allows the Unicode filename but the file doesn't
4349
# The filesystem allows the Unicode filename and the file exists,
4353
UnicodeFilenameFeature = _UnicodeFilenameFeature()
4356
class _CompatabilityThunkFeature(Feature):
4357
"""This feature is just a thunk to another feature.
4359
It issues a deprecation warning if it is accessed, to let you know that you
4360
should really use a different feature.
4363
def __init__(self, dep_version, module, name,
4364
replacement_name, replacement_module=None):
4365
super(_CompatabilityThunkFeature, self).__init__()
4366
self._module = module
4367
if replacement_module is None:
4368
replacement_module = module
4369
self._replacement_module = replacement_module
4371
self._replacement_name = replacement_name
4372
self._dep_version = dep_version
4373
self._feature = None
4376
if self._feature is None:
4377
depr_msg = self._dep_version % ('%s.%s'
4378
% (self._module, self._name))
4379
use_msg = ' Use %s.%s instead.' % (self._replacement_module,
4380
self._replacement_name)
4381
symbol_versioning.warn(depr_msg + use_msg, DeprecationWarning)
4382
# Import the new feature and use it as a replacement for the
4384
self._feature = pyutils.get_named_object(
4385
self._replacement_module, self._replacement_name)
4389
return self._feature._probe()
4392
class ModuleAvailableFeature(Feature):
4393
"""This is a feature than describes a module we want to be available.
4395
Declare the name of the module in __init__(), and then after probing, the
4396
module will be available as 'self.module'.
4398
:ivar module: The module if it is available, else None.
4401
def __init__(self, module_name):
4402
super(ModuleAvailableFeature, self).__init__()
4403
self.module_name = module_name
4407
self._module = __import__(self.module_name, {}, {}, [''])
4414
if self.available(): # Make sure the probe has been done
4418
def feature_name(self):
4419
return self.module_name
4422
4273
def probe_unicode_in_user_encoding():
4423
4274
"""Try to encode several unicode strings to use in unicode-aware tests.
4424
4275
Return first successfull match.
4455
class _HTTPSServerFeature(Feature):
4456
"""Some tests want an https Server, check if one is available.
4458
Right now, the only way this is available is under python2.6 which provides
4469
def feature_name(self):
4470
return 'HTTPSServer'
4473
HTTPSServerFeature = _HTTPSServerFeature()
4476
class _UnicodeFilename(Feature):
4477
"""Does the filesystem support Unicode filenames?"""
4482
except UnicodeEncodeError:
4484
except (IOError, OSError):
4485
# The filesystem allows the Unicode filename but the file doesn't
4489
# The filesystem allows the Unicode filename and the file exists,
4493
UnicodeFilename = _UnicodeFilename()
4496
class _ByteStringNamedFilesystem(Feature):
4497
"""Is the filesystem based on bytes?"""
4500
if os.name == "posix":
4504
ByteStringNamedFilesystem = _ByteStringNamedFilesystem()
4507
class _UTF8Filesystem(Feature):
4508
"""Is the filesystem UTF-8?"""
4511
if osutils._fs_enc.upper() in ('UTF-8', 'UTF8'):
4515
UTF8Filesystem = _UTF8Filesystem()
4518
class _BreakinFeature(Feature):
4519
"""Does this platform support the breakin feature?"""
4522
from bzrlib import breakin
4523
if breakin.determine_signal() is None:
4525
if sys.platform == 'win32':
4526
# Windows doesn't have os.kill, and we catch the SIGBREAK signal.
4527
# We trigger SIGBREAK via a Console api so we need ctypes to
4528
# access the function
4535
def feature_name(self):
4536
return "SIGQUIT or SIGBREAK w/ctypes on win32"
4539
BreakinFeature = _BreakinFeature()
4542
class _CaseInsCasePresFilenameFeature(Feature):
4543
"""Is the file-system case insensitive, but case-preserving?"""
4546
fileno, name = tempfile.mkstemp(prefix='MixedCase')
4548
# first check truly case-preserving for created files, then check
4549
# case insensitive when opening existing files.
4550
name = osutils.normpath(name)
4551
base, rel = osutils.split(name)
4552
found_rel = osutils.canonical_relpath(base, name)
4553
return (found_rel == rel
4554
and os.path.isfile(name.upper())
4555
and os.path.isfile(name.lower()))
4560
def feature_name(self):
4561
return "case-insensitive case-preserving filesystem"
4563
CaseInsCasePresFilenameFeature = _CaseInsCasePresFilenameFeature()
4566
class _CaseInsensitiveFilesystemFeature(Feature):
4567
"""Check if underlying filesystem is case-insensitive but *not* case
4570
# Note that on Windows, Cygwin, MacOS etc, the file-systems are far
4571
# more likely to be case preserving, so this case is rare.
4574
if CaseInsCasePresFilenameFeature.available():
4577
if TestCaseWithMemoryTransport.TEST_ROOT is None:
4578
root = osutils.mkdtemp(prefix='testbzr-', suffix='.tmp')
4579
TestCaseWithMemoryTransport.TEST_ROOT = root
4581
root = TestCaseWithMemoryTransport.TEST_ROOT
4582
tdir = osutils.mkdtemp(prefix='case-sensitive-probe-', suffix='',
4584
name_a = osutils.pathjoin(tdir, 'a')
4585
name_A = osutils.pathjoin(tdir, 'A')
4587
result = osutils.isdir(name_A)
4588
_rmtree_temp_dir(tdir)
4591
def feature_name(self):
4592
return 'case-insensitive filesystem'
4594
CaseInsensitiveFilesystemFeature = _CaseInsensitiveFilesystemFeature()
4597
class _CaseSensitiveFilesystemFeature(Feature):
4600
if CaseInsCasePresFilenameFeature.available():
4602
elif CaseInsensitiveFilesystemFeature.available():
4607
def feature_name(self):
4608
return 'case-sensitive filesystem'
4610
# new coding style is for feature instances to be lowercase
4611
case_sensitive_filesystem_feature = _CaseSensitiveFilesystemFeature()
4614
4306
# Only define SubUnitBzrRunner if subunit is available.
4616
4308
from subunit import TestProtocolClient