def setUp(self): # Disable log messages to silence expected warnings cf.log_level('DISABLE') # Note: to enable all messages for given methods, lines or # calls (those without a 'verbose' option to do the same) # e.g. to debug them, wrap them (for methods, start-to-end # internally) as follows: # # cf.LOG_LEVEL('DEBUG') # < ... test code ... > # cf.log_level('DISABLE') self.indexed = 'DSG_timeSeries_indexed.nc'
def test_aliases(self): self.assertEqual(cf.log_level(), cf.LOG_LEVEL()) self.assertEqual(cf.free_memory(), cf.FREE_MEMORY()) self.assertEqual(cf.free_memory_factor(), cf.FREE_MEMORY_FACTOR()) self.assertEqual(cf.fm_threshold(), cf.FM_THRESHOLD()) self.assertEqual(cf.total_memory(), cf.TOTAL_MEMORY()) self.assertEqual(cf.regrid_logging(), cf.REGRID_LOGGING()) self.assertEqual(cf.relaxed_identities(), cf.RELAXED_IDENTITIES()) self.assertEqual(cf.tempdir(), cf.TEMPDIR()) self.assertEqual(cf.chunksize(), cf.CHUNKSIZE()) self.assertEqual(cf.set_performance(), cf.SET_PERFORMANCE()) self.assertEqual(cf.of_fraction(), cf.OF_FRACTION()) self.assertEqual(cf.collapse_parallel_mode(), cf.COLLAPSE_PARALLEL_MODE())
def test_manage_log_level_via_verbose_attr(self): if self.test_only and inspect.stack()[0][3] not in self.test_only: return # Order of decreasing severity/verbosity is crucial to one test below levels = ['WARNING', 'INFO', 'DETAIL', 'DEBUG'] # Note we test assertions on the root logger object, which is the # one output overall at runtime, but the specific module logger name # should be registered within the log message: example_class = dummyClass() log_message = [ 'WARNING:{}:{}'.format(log_name, example_class.warning_message), 'INFO:{}:{}'.format(log_name, example_class.info_message), 'DETAIL:{}:{}'.format(log_name, example_class.detail_message), 'DEBUG:{}:{}'.format(log_name, example_class.debug_message) ] for level in levels: # Important! Need to initialise class inside this loop not # outside it or, it retains the verbosity attribute value set # for the previous loop (0, i.e disable, so nothing emerges!) test_class = dummyClass() cf.log_level(level) # reset to level # Default verbose(=None) cases: log_level should determine output with self.assertLogs(level=cf.log_level()) as catch: test_class.decorated_logging_func() for msg in log_message: # log_level should prevent messages less severe appearing: if levels.index(level) >= log_message.index(msg): self.assertIn(msg, catch.output) else: # less severe, should be effectively filtered out self.assertNotIn(msg, catch.output) # Cases where verbose is set; value should override log_level... # Highest verbosity case (note -1 == 'DEBUG', highest verbosity): # all messages should appear, regardless of global log_level: for attr in (-1, 'DEBUG', 'debug', 'Debug', 'DeBuG'): test_class.verbose = attr with self.assertLogs(level=cf.log_level()) as catch: test_class.decorated_logging_func() for msg in log_message: self.assertIn(msg, catch.output) # Lowest verbosity case ('WARNING' / 1) excluding special case of # 'DISABLE' (see note above): only warning messages should appear, # regardless of global log_level value set: for attr in (1, 'WARNING', 'warning', 'Warning', 'WaRning'): test_class.verbose = attr with self.assertLogs(level=cf.log_level()) as catch: test_class.decorated_logging_func() for msg in log_message: if msg.split(":")[0] == 'WARNING': self.assertIn(msg, catch.output) else: self.assertNotIn(msg, catch.output) # Boolean cases for testing backwards compatibility... # ... verbose=2 should be equivalent to verbose=3 now: test_class.verbose = True with self.assertLogs(level=cf.log_level()) as catch: test_class.decorated_logging_func() for msg in log_message: if msg.split(":")[0] == 'DEBUG': self.assertNotIn(msg, catch.output) else: self.assertIn(msg, catch.output) # ... verbose=0 should be equivalent to verbose=0 now, so # test along with 'DISABLE' special case below... # Special 'DISABLE' (0) case: note this needs to be last as we # reset the log_level to it but need to use 'NOTSET' for the # assertLogs level, which sends all log messages through: for attr in (0, 'DISABLE', 'disable', 'Disable', 'DisAblE'): test_class.verbose = attr with self.assertLogs(level='NOTSET') as catch: # Note: get 'AssertionError' if don't log anything at all, # so to avoid this and allow check for disabled logging, # first log something then disable and check that no other # messages emerge: logger.info( "Purely to keep 'assertLog' happy: see comment!") cf.log_level('DISABLE') test_class.decorated_logging_func() for msg in log_message: # nothing else should be logged self.assertNotIn(msg, catch.output) # verbose=0 should be equivalent in behaviour to verbose=0 test_class.verbose = False with self.assertLogs(level='NOTSET') as catch: logger.info("Purely to keep 'assertLog' happy: see previous!") test_class.decorated_logging_func() for msg in log_message: # nothing else should be logged self.assertNotIn(msg, catch.output)
def test_configuration(self): # This test assumes 'total_memory' remains constant throughout # the test run, which should be true generally in any # reasonable context. # Test getting of all config. and store original values to # test on: org = cf.configuration() self.assertIsInstance(org, dict) # Check all keys that should be there are, with correct value type: self.assertEqual(len(org), 11) # update expected len if add new key(s) # Floats expected as values for most keys. Store these for # later as floats need assertAlmostEqual rather than # assertEqual tests: keys_with_float_values = [ "atol", "rtol", "of_fraction", "free_memory_factor", "chunksize", ] for key in keys_with_float_values: self.assertIsInstance(org[key], float) # Other types expected: self.assertIsInstance(org["collapse_parallel_mode"], int) self.assertIsInstance(org["relaxed_identities"], bool) self.assertIsInstance(org["bounds_combination_mode"], str) self.assertIsInstance(org["regrid_logging"], bool) # Log level may be input as an int but always given as # equiv. string self.assertIsInstance(org["log_level"], str) self.assertIsInstance(org["tempdir"], str) # Store some sensible values to reset items to for testing, ensuring: # 1) they are kept different to the defaults (i.e. org values); and # 2) floats differ sufficiently that they will be picked up as # qdifferent by the assertAlmostEqual decimal places (8, see # below) reset_values = { "rtol": 5e-7, "atol": 2e-7, "tempdir": "/my-custom-tmpdir", "of_fraction": 0.1, "free_memory_factor": 0.25, "regrid_logging": True, "collapse_parallel_mode": 2, "relaxed_identities": True, "bounds_combination_mode": "XOR", "log_level": "INFO", "chunksize": 8e9, } # Test the setting of each lone item. expected_post_set = dict(org) # copy for safety with mutable dict for setting, value in reset_values.items(): cf.configuration(**{setting: value}) post_set = cf.configuration() # Expect a dict that is identical to the original to start # with but as we set values incrementally they should be # reflected: expected_post_set[setting] = value # Can't trivially do a direct test that the actual and # expected return dicts are the same as there are float # values which have limited float precision so need # assertAlmostEqual testing: for name, val in expected_post_set.items(): if isinstance(val, float): self.assertAlmostEqual(post_set[name], val, places=8, msg=setting) else: self.assertEqual(post_set[name], val) # Test the setting of more than one, but not all, items # simultaneously: new_values = { "regrid_logging": True, "tempdir": "/bin/bag", "of_fraction": 0.33, } cf.configuration(**new_values) post_set = cf.configuration() for name, val in new_values.items(): # test values that should change self.assertEqual(post_set[name], val) # ...and some values that should not: self.assertEqual(post_set["log_level"], "INFO") self.assertAlmostEqual(post_set["rtol"], 5e-7) # Test setting all possible items simultaneously (back to originals): cf.configuration(**org) post_set = cf.configuration() for name, val in org.items(): if isinstance(val, float): self.assertAlmostEqual(post_set[name], val, places=8) else: self.assertEqual(post_set[name], val) # Test edge cases & invalid inputs... # ... 1. Falsy value inputs on some representative items: pre_set_config = cf.configuration() with self.assertRaises(ValueError): cf.configuration(of_fraction=0.0) with self.assertRaises(ValueError): cf.configuration(free_memory_factor=0.0) new_values = { "tempdir": "", "atol": 0.0, "regrid_logging": False, } cf.configuration(**new_values) post_set = cf.configuration() for name, val in new_values.items(): # test values that should change self.assertEqual(post_set[name], val) # ...and some values that should not: self.assertEqual(post_set["log_level"], pre_set_config["log_level"]) self.assertAlmostEqual(post_set["rtol"], pre_set_config["rtol"]) # 2. None as an input kwarg rather than as a default: pre_set_config = cf.configuration() set_of = 0.45 cf.configuration(of_fraction=set_of, rtol=None, log_level=None) post_set = cf.configuration() # test values that should change self.assertEqual(post_set["of_fraction"], set_of) # ...and values that should not: self.assertEqual(post_set["rtol"], pre_set_config["rtol"]) self.assertAlmostEqual(post_set["log_level"], pre_set_config["log_level"]) # 3. Gracefully error with invalid inputs: with self.assertRaises(ValueError): cf.configuration(of_fraction="bad") with self.assertRaises(ValueError): cf.configuration(log_level=7) # 4. Check invalid kwarg given logic processes **kwargs: with self.assertRaises(TypeError): cf.configuration(bad_kwarg=1e-15) old = cf.configuration() try: cf.configuration(atol=888, rtol=999, log_level="BAD") except ValueError: self.assertEqual(cf.configuration(), old) else: raise RuntimeError( "A ValueError should have been raised, but wasn't") # Reset so later test fixtures don't spam with output # messages: cf.log_level("DISABLE")
def setUp(self): # Disable log messages to silence expected warnings cf.log_level("DISABLE")
def test_configuration(self): if self.test_only and inspect.stack()[0][3] not in self.test_only: return # This test assumes 'total_memory' remains constant throughout the # test run, which should be true generally in any reasonable context. # Test getting of all config. and store original values to test on: org = cf.configuration() self.assertIsInstance(org, dict) # Check all keys that should be there are, with correct value type: self.assertEqual(len(org), 13) # update expected len if add new key(s) # Floats expected as values for most keys. Store these for later as # floats need assertAlmostEqual rather than assertEqual tests: keys_with_float_values = [ 'atol', 'rtol', 'of_fraction', 'total_memory', 'free_memory_factor', 'fm_threshold', 'min_total_memory', 'chunksize', ] for key in keys_with_float_values: self.assertIsInstance(org[key], float) # Other types expected: self.assertIsInstance(org['collapse_parallel_mode'], int) self.assertIsInstance(org['relaxed_identities'], bool) self.assertIsInstance(org['regrid_logging'], bool) # Log level may be input as an int but always given as equiv. string self.assertIsInstance(org['log_level'], str) self.assertIsInstance(org['tempdir'], str) constants_that_cannot_be_set = ( 'total_memory', 'fm_threshold', 'min_total_memory', ) # Store some sensible values to reset items to for testing, ensuring: # 1) they are kept different to the defaults (i.e. org values); and # 2) floats differ sufficiently that they will be picked up as # different by the assertAlmostEqual decimal places (8, see below) reset_values = { 'rtol': 5e-7, 'atol': 2e-7, 'tempdir': '/my-custom-tmpdir', 'of_fraction': 0.1, 'total_memory': 5e10, # can't in fact be (re)set: test for error 'free_memory_factor': 0.25, 'regrid_logging': True, 'collapse_parallel_mode': 2, 'relaxed_identities': True, 'log_level': 'INFO', 'fm_threshold': 4e9, # also can't be (re)set 'min_total_memory': 6e9, # also can't be (re)set 'chunksize': 8e9, } # Test the setting of each lone item. expected_post_set = dict(org) # copy for safety with mutable dict for setting, value in reset_values.items(): # These are shown in output but can't be set (no such kwarg): if setting in constants_that_cannot_be_set: with self.assertRaises(TypeError): # error from invalid kwarg cf.configuration(**{setting: value}) continue cf.configuration(**{setting: value}) post_set = cf.configuration() keys_with_float_values # Expect a dict that is identical to the original to start with # but as we set values incrementally they should be reflected: expected_post_set[setting] = value # As a special case, we need to account for the fact that # fm_threshold = free_memory_factor * total_memory, so it # changes when the former is set (latter can't be set): if setting == 'free_memory_factor': expected_post_set['fm_threshold'] = ( value * expected_post_set['total_memory']) # Can't trivially do a direct test that the actual and expected # return dicts are the same as there are float values which have # limited float precision so need assertAlmostEqual testing: for name, val in expected_post_set.items(): if isinstance(val, float): self.assertAlmostEqual(post_set[name], val, places=8) else: self.assertEqual(post_set[name], val) # Test the setting of more than one, but not all, items simultaneously: new_values = { 'regrid_logging': True, 'tempdir': '/bin/bag', 'of_fraction': 0.33, } cf.configuration(**new_values) post_set = cf.configuration() for name, val in new_values.items(): # test values that should change self.assertEqual(post_set[name], val) # ...and some values that should not: self.assertEqual(post_set['log_level'], 'INFO') self.assertAlmostEqual(post_set['rtol'], 5e-7) # Test setting all possible items simultaneously (back to originals): for constant_name in constants_that_cannot_be_set: org.pop(constant_name) # as these can't be set, are just shown cf.configuration(**org) post_set = cf.configuration() for name, val in org.items(): if isinstance(val, float): self.assertAlmostEqual(post_set[name], val, places=8) else: self.assertEqual(post_set[name], val) # Test edge cases & invalid inputs... # ... 1. Falsy value inputs on some representative items: pre_set_config = cf.configuration() with self.assertRaises(ValueError): cf.configuration(of_fraction=0.0) with self.assertRaises(ValueError): cf.configuration(free_memory_factor=0.0) new_values = { 'tempdir': '', 'atol': 0.0, 'regrid_logging': False, } cf.configuration(**new_values) post_set = cf.configuration() for name, val in new_values.items(): # test values that should change self.assertEqual(post_set[name], val) # ...and some values that should not: self.assertEqual(post_set['log_level'], pre_set_config['log_level']) self.assertAlmostEqual(post_set['rtol'], pre_set_config['rtol']) # 2. None as an input kwarg rather than as a default: pre_set_config = cf.configuration() set_of = 0.45 cf.configuration(of_fraction=set_of, rtol=None, log_level=None) post_set = cf.configuration() # test values that should change self.assertEqual(post_set['of_fraction'], set_of) # ...and values that should not: self.assertEqual(post_set['rtol'], pre_set_config['rtol']) self.assertAlmostEqual(post_set['log_level'], pre_set_config['log_level']) # 3. Gracefully error with invalid inputs: with self.assertRaises(ValueError): cf.configuration(of_fraction='bad') with self.assertRaises(ValueError): cf.configuration(log_level=7) # 4. Check invalid kwarg given logic processes **kwargs: with self.assertRaises(TypeError): cf.configuration(bad_kwarg=1e-15) # Reset so later test fixtures don't spam with output messages: cf.log_level('DISABLE')
print("\n**Tutorial**\n") print("\n**Import**\n") import cf cf.log_level('INFO') cf.CF() print("\n**Field construct**\n") print("\n**Reading field constructs from datasets**\n") x = cf.read('file.nc') type(x) len(x) y = cf.read('*.nc') len(y) z = cf.read(['file.nc', 'precipitation_flux.nc']) len(z) try: y = cf.read('$PWD') except: pass else: raise Exception("This should have failed!") y = cf.read('$PWD', ignore_read_error=True) len(y) print("\n**Inspection**\n")