There is an important thing to watch out for when using the tuple() constructor method instead of the default type definition for creating your single-string tuples. Here is a Nose2/Unittest script you can use to play with the problem:
#!/usr/bin/env python
# vim: ts=4 sw=4 sts=4 et
from __future__ import print_function
# global
import unittest
import os
import sys
import logging
import pprint
import shutil
# module-level logger
logger = logging.getLogger(__name__)
# module-global test-specific imports
# where to put test output data for compare.
testdatadir = os.path.join('.', 'test', 'test_data')
rawdata_dir = os.path.join(os.path.expanduser('~'), 'Downloads')
testfiles = (
'bogus.data',
)
purge_results = False
output_dir = os.path.join('test_data', 'example_out')
def cleanPath(path):
'''cleanPath
Recursively removes everything below a path
:param path:
the path to clean
'''
for root, dirs, files in os.walk(path):
for fn in files:
logger.debug('removing {}'.format(fn))
os.unlink(os.path.join(root, fn))
for dn in dirs:
# recursive
try:
logger.debug('recursive del {}'.format(dn))
shutil.rmtree(os.path.join(root, dn))
except Exception:
# for now, halt on all. Override with shutil onerror
# callback and ignore_errors.
raise
class TestChangeMe(unittest.TestCase):
'''
TestChangeMe
'''
testdatadir = None
rawdata_dir = None
testfiles = None
output_dir = output_dir
def __init__(self, *args, **kwargs):
self.testdatadir = os.path.join(os.path.dirname(
os.path.abspath(__file__)), testdatadir)
super(TestChangeMe, self).__init__(*args, **kwargs)
# check for kwargs
# this allows test control by instance
self.testdatadir = kwargs.get('testdatadir', testdatadir)
self.rawdata_dir = kwargs.get('rawdata_dir', rawdata_dir)
self.testfiles = kwargs.get('testfiles', testfiles)
self.output_dir = kwargs.get('output_dir', output_dir)
def setUp(self):
'''setUp
pre-test setup called before each test
'''
logging.debug('setUp')
if not os.path.exists(self.testdatadir):
os.mkdir(self.testdatadir)
else:
self.assertTrue(os.path.isdir(self.testdatadir))
self.assertTrue(os.path.exists(self.testdatadir))
cleanPath(self.output_dir)
def tearDown(self):
'''tearDown
post-test cleanup, if required
'''
logging.debug('tearDown')
if purge_results:
cleanPath(self.output_dir)
def tupe_as_arg(self, tuple1, tuple2, tuple3, tuple4):
'''test_something_0
auto-run tests sorted by ascending alpha
'''
# for testing, recreate strings and lens
string1 = 'string number 1'
len_s1 = len(string1)
string2 = 'string number 2'
len_s2 = len(string2)
# run the same tests...
# should test as type = string
self.assertTrue(type(tuple1) == str)
self.assertFalse(type(tuple1) == tuple)
self.assertEqual(len_s1, len_s2, len(tuple1))
self.assertEqual(len(tuple2), 1)
# this will fail
# self.assertEqual(len(tuple4), 1)
self.assertEqual(len(tuple3), 2)
self.assertTrue(type(string1) == str)
self.assertTrue(type(string2) == str)
self.assertTrue(string1 == tuple1)
# should test as type == tuple
self.assertTrue(type(tuple2) == tuple)
self.assertTrue(type(tuple4) == tuple)
self.assertFalse(type(tuple1) == type(tuple2))
self.assertFalse(type(tuple1) == type(tuple4))
# this will fail
# self.assertFalse(len(tuple4) == len(tuple1))
self.assertFalse(len(tuple2) == len(tuple1))
def default_test(self):
'''testFileDetection
Tests all data files for type and compares the results to the current
stored results.
'''
# test 1
__import__('pudb').set_trace()
string1 = 'string number 1'
len_s1 = len(string1)
string2 = 'string number 2'
len_s2 = len(string2)
tuple1 = (string1)
tuple2 = (string1,)
tuple3 = (string1, string2)
tuple4 = tuple(string1,)
# should test as type = string
self.assertTrue(type(tuple1) == str)
self.assertFalse(type(tuple1) == tuple)
self.assertEqual(len_s1, len_s2, len(tuple1))
self.assertEqual(len(tuple2), 1)
# this will fail
# self.assertEqual(len(tuple4), 1)
self.assertEqual(len(tuple3), 2)
self.assertTrue(type(string1) == str)
self.assertTrue(type(string2) == str)
self.assertTrue(string1 == tuple1)
# should test as type == tuple
self.assertTrue(type(tuple2) == tuple)
self.assertTrue(type(tuple4) == tuple)
self.assertFalse(type(tuple1) == type(tuple2))
self.assertFalse(type(tuple1) == type(tuple4))
# this will fail
# self.assertFalse(len(tuple4) == len(tuple1))
self.assertFalse(len(tuple2) == len(tuple1))
self.tupe_as_arg(tuple1, tuple2, tuple3, tuple4)
# stand-alone test execution
if __name__ == '__main__':
import nose2
nose2.main(
argv=[
'fake',
'--log-capture',
'TestChangeMe.default_test',
])
You will notice that the (nearly) identical code calling tuple(string1,) shows as type tuple, but the length will be the same as the string length and all members will be single characters.
This will cause the assertions on lines #137, #147, #104 and #115 to fail, even though they are seemingly identical to the ones that pass.
(note: I have a PUDB breakpoint in the code at line #124, it's an excellent debug tool, but you can remove it if you prefer. Otherwise simply pip install pudb
to use it.)
re
module will return an individual element if there's only one regex match, or a tuple of them if there's more than one. – Brewer%
operator. I would file a bug. – Chinook