taxcalc 4.4.0__py3-none-any.whl → 4.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. taxcalc/__init__.py +1 -1
  2. taxcalc/calcfunctions.py +326 -171
  3. taxcalc/calculator.py +35 -34
  4. taxcalc/cli/tc.py +6 -7
  5. taxcalc/consumption.py +9 -4
  6. taxcalc/data.py +8 -8
  7. taxcalc/decorators.py +3 -3
  8. taxcalc/growdiff.py +5 -0
  9. taxcalc/growfactors.py +1 -1
  10. taxcalc/parameters.py +85 -42
  11. taxcalc/policy.py +1 -1
  12. taxcalc/records.py +1 -0
  13. taxcalc/records_variables.json +6 -0
  14. taxcalc/taxcalcio.py +49 -44
  15. taxcalc/tests/cmpi_cps_expect.txt +6 -6
  16. taxcalc/tests/cmpi_puf_expect.txt +6 -6
  17. taxcalc/tests/conftest.py +42 -41
  18. taxcalc/tests/test_4package.py +9 -7
  19. taxcalc/tests/test_benefits.py +9 -8
  20. taxcalc/tests/test_calcfunctions.py +55 -38
  21. taxcalc/tests/test_calculator.py +11 -6
  22. taxcalc/tests/test_compare.py +44 -50
  23. taxcalc/tests/test_compatible_data.py +9 -7
  24. taxcalc/tests/test_consumption.py +38 -18
  25. taxcalc/tests/test_cpscsv.py +33 -31
  26. taxcalc/tests/test_data.py +31 -24
  27. taxcalc/tests/test_decorators.py +84 -32
  28. taxcalc/tests/test_growdiff.py +16 -13
  29. taxcalc/tests/test_growfactors.py +8 -8
  30. taxcalc/tests/test_parameters.py +54 -58
  31. taxcalc/tests/test_policy.py +14 -12
  32. taxcalc/tests/test_puf_var_stats.py +14 -14
  33. taxcalc/tests/test_pufcsv.py +40 -40
  34. taxcalc/tests/test_records.py +73 -60
  35. taxcalc/tests/test_reforms.py +34 -31
  36. taxcalc/tests/test_responses.py +4 -4
  37. taxcalc/tests/test_taxcalcio.py +76 -62
  38. taxcalc/tests/test_utils.py +78 -46
  39. taxcalc/utils.py +49 -42
  40. taxcalc/validation/taxsim35/taxsim_emulation.json +1 -5
  41. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/METADATA +19 -5
  42. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/RECORD +46 -46
  43. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/WHEEL +1 -1
  44. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/LICENSE +0 -0
  45. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/entry_points.txt +0 -0
  46. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/top_level.txt +0 -0
@@ -13,15 +13,11 @@ import tempfile
13
13
  import numpy as np
14
14
  import paramtools
15
15
  import pytest
16
- # pylint: disable=import-error
17
- from taxcalc import (
18
- Parameters,
19
- Policy,
20
- Consumption,
21
- GrowDiff,
22
- GrowFactors,
23
- is_paramtools_format,
24
- )
16
+ from taxcalc.parameters import Parameters, is_paramtools_format
17
+ from taxcalc.policy import Policy
18
+ from taxcalc.consumption import Consumption
19
+ from taxcalc.growdiff import GrowDiff
20
+ from taxcalc.growfactors import GrowFactors
25
21
 
26
22
 
27
23
  # Test specification and use of simple Parameters-derived class that has
@@ -129,6 +125,7 @@ def test_params_class(revision, expect, params_json_file):
129
125
  """
130
126
  The Params class is derived from the abstract base Parameter class.
131
127
  """
128
+ # pylint: disable=too-few-public-methods,abstract-method
132
129
  DEFAULTS_FILE_NAME = params_json_file.name
133
130
  DEFAULTS_FILE_PATH = ''
134
131
  START_YEAR = 2001
@@ -157,8 +154,8 @@ def test_params_class(revision, expect, params_json_file):
157
154
  assert prms.start_year == 2001
158
155
  assert prms.current_year == 2001
159
156
  assert prms.end_year == 2010
160
- assert prms.inflation_rates() == list()
161
- assert prms.wage_growth_rates() == list()
157
+ assert prms.inflation_rates() == []
158
+ assert prms.wage_growth_rates() == []
162
159
  prms.set_year(2010)
163
160
  assert prms.current_year == 2010
164
161
  with pytest.raises(paramtools.ValidationError):
@@ -189,24 +186,17 @@ def test_json_file_contents(tests_path, fname):
189
186
  """
190
187
  Check contents of JSON parameter files in Tax-Calculator/taxcalc directory.
191
188
  """
189
+ # pylint: disable=too-many-locals,too-many-branches
192
190
  first_year = Policy.JSON_START_YEAR
193
191
  last_known_year = Policy.LAST_KNOWN_YEAR # for indexed parameter values
194
192
  known_years = set(range(first_year, last_known_year + 1))
195
- long_params = ['II_brk1', 'II_brk2', 'II_brk3', 'II_brk4',
196
- 'II_brk5', 'II_brk6', 'II_brk7',
197
- 'PT_brk1', 'PT_brk2', 'PT_brk3', 'PT_brk4',
198
- 'PT_brk5', 'PT_brk6', 'PT_brk7',
199
- 'PT_qbid_taxinc_thd',
200
- 'ALD_BusinessLosses_c',
201
- 'STD', 'II_em', 'II_em_ps',
202
- 'AMT_em', 'AMT_em_ps', 'AMT_em_pe',
203
- 'ID_ps', 'ID_AllTaxes_c']
204
193
  # for TCJA-reverting long_params
205
194
  long_known_years = set(range(first_year, last_known_year + 1))
206
195
  long_known_years.add(2026)
207
196
  # check elements in each parameter sub-dictionary
208
197
  failures = ''
209
- with open(os.path.join(tests_path, "..", fname)) as f:
198
+ path = os.path.join(tests_path, "..", fname)
199
+ with open(path, 'r', encoding='utf-8') as f:
210
200
  allparams = json.loads(f.read())
211
201
  for pname in allparams:
212
202
  if pname == "schema":
@@ -219,24 +209,29 @@ def test_json_file_contents(tests_path, fname):
219
209
  assert param.get('indexed', False) is False
220
210
  # check that indexable is True when indexed is True
221
211
  if param.get('indexed', False) and not param.get('indexable', False):
222
- msg = 'param:<{}>; indexed={}; indexable={}'
223
- fail = msg.format(pname,
224
- param.get('indexed', False),
225
- param.get('indexable', False))
226
- failures += fail + '\n'
212
+ msg = (
213
+ f'param:<{pname}>; '
214
+ f'indexed={param.get("indexed", False)}; '
215
+ f'indexable={param.get("indexable", False)}\n'
216
+ )
217
+ failures += msg
227
218
  # check that indexable param has value_type float
228
219
  if param.get('indexable', False) and param['type'] != 'float':
229
- msg = 'param:<{}>; type={}; indexable={}'
230
- fail = msg.format(pname, param['type'],
231
- param.get('indexable', False))
232
- failures += fail + '\n'
220
+ msg = (
221
+ f'param:<{pname}>; '
222
+ f'type={param["type"]}; '
223
+ f'indexable={param.get("indexable", False)}\n'
224
+ )
225
+ failures += msg
233
226
  # ensure that indexable is False when value_type is not real
234
227
  if param.get('indexable', False) and param['type'] != 'float':
235
- msg = 'param:<{}>; indexable={}; type={}'
236
- fail = msg.format(pname,
237
- param.get('indexable', False),
238
- param['value_type'])
239
- failures += fail + '\n'
228
+ msg = (
229
+ f'param:<{pname}>; '
230
+ f'indexable={param.get("indexable", False)}; '
231
+ f'type={param["value_type"]}\n'
232
+ )
233
+ failures += msg
234
+ o = None
240
235
  if fname == "consumption.json":
241
236
  o = Consumption()
242
237
  elif fname == "policy_current_law.json":
@@ -251,9 +246,8 @@ def test_json_file_contents(tests_path, fname):
251
246
  for y in known_years:
252
247
  o.set_year(y)
253
248
  if np.isnan(getattr(o, param)).any():
254
- msg = 'param:<{}>; not found in year={}'
255
- fail = msg.format(param, y)
256
- failures += fail + '\n'
249
+ msg = f'param:<{param}>; not found in year={y}\n'
250
+ failures += msg
257
251
  if failures:
258
252
  raise ValueError(failures)
259
253
 
@@ -268,29 +262,29 @@ def test_parameters_mentioned(tests_path, jfname, pfname):
268
262
  """
269
263
  # read JSON parameter file into a dictionary
270
264
  path = os.path.join(tests_path, '..', jfname)
271
- pfile = open(path, 'r')
272
- allparams = json.load(pfile)
273
- pfile.close()
265
+ with open(path, 'r', encoding='utf-8') as pfile:
266
+ allparams = json.load(pfile)
274
267
  assert isinstance(allparams, dict)
275
268
  # read PYTHON code file text
269
+ # pylint: disable=consider-using-join
276
270
  if pfname == 'consumption.py':
277
271
  # consumption.py does not explicitly name the parameters
278
272
  code_text = ''
279
273
  for var in Consumption.RESPONSE_VARS:
280
- code_text += 'MPC_{}\n'.format(var)
274
+ code_text += f'MPC_{var}\n'
281
275
  for var in Consumption.BENEFIT_VARS:
282
- code_text += 'BEN_{}_value\n'.format(var)
276
+ code_text += f'BEN_{var}_value\n'
283
277
  elif pfname == 'growdiff.py':
284
278
  # growdiff.py does not explicitly name the parameters
285
279
  code_text = ''
286
280
  for var in GrowFactors.VALID_NAMES:
287
- code_text += '{}\n'.format(var)
281
+ code_text += f'{var}\n'
288
282
  else:
289
283
  # parameters are explicitly named in PYTHON file
290
284
  path = os.path.join(tests_path, '..', pfname)
291
- pfile = open(path, 'r')
292
- code_text = pfile.read()
293
- pfile.close()
285
+ with open(path, 'r', encoding='utf-8') as pfile:
286
+ code_text = pfile.read()
287
+ # pylint: enable=consider-using-join
294
288
  # check that each param (without leading _) is mentioned in code text
295
289
  for pname in allparams:
296
290
  if pname == "schema":
@@ -301,6 +295,7 @@ def test_parameters_mentioned(tests_path, jfname, pfname):
301
295
  # following tests access private methods, so pylint: disable=protected-access
302
296
 
303
297
  class ArrayParams(Parameters):
298
+ """ArrayParams class"""
304
299
  defaults = {
305
300
  "schema": {
306
301
  "labels": {
@@ -407,7 +402,7 @@ class ArrayParams(Parameters):
407
402
  self._update(revision, print_warnings, raise_errors)
408
403
 
409
404
  def set_rates(self):
410
- pass
405
+ """Method docstring"""
411
406
 
412
407
 
413
408
  def test_expand_xd_errors():
@@ -420,6 +415,7 @@ def test_expand_xd_errors():
420
415
 
421
416
 
422
417
  def test_expand_empty():
418
+ """Test docstring"""
423
419
  params = ArrayParams(label_to_extend=None, array_first=False)
424
420
  params.sort_values()
425
421
  one_dim = copy.deepcopy(params.one_dim)
@@ -431,6 +427,7 @@ def test_expand_empty():
431
427
 
432
428
 
433
429
  def test_expand_1d_scalar():
430
+ """Test docstring"""
434
431
  yrs = 12
435
432
  val = 10.0
436
433
  exp = np.array([val * math.pow(1.02, i) for i in range(0, yrs)])
@@ -453,7 +450,6 @@ def test_expand_2d_short_array():
453
450
  """
454
451
  One of several _expand_?D tests.
455
452
  """
456
- ary = np.array([[1., 2., 3.]])
457
453
  val = np.array([1., 2., 3.])
458
454
  exp2 = np.array([val * math.pow(1.02, i) for i in range(1, 5)])
459
455
  exp1 = np.array([1., 2., 3.])
@@ -563,19 +559,19 @@ def test_expand_2d_partial_expand():
563
559
  assert np.allclose(res, exp, atol=0.01, rtol=0.0)
564
560
 
565
561
 
566
- taxcalc_revision = """
562
+ TAXCALC_REVISION = """
567
563
  {
568
564
  "consumption": {"BEN_mcaid_value": {"2013": 0.9}}
569
565
  }
570
566
  """
571
567
 
572
- paramtools_revision = """
568
+ PARAMTOOLS_REVISION = """
573
569
  {
574
570
  "consumption": {"BEN_mcaid_value": [{"year": "2013", "value": 0.9}]}
575
571
  }
576
572
  """
577
573
 
578
- paramtools_revision2 = """
574
+ PARAMTOOLS_REVISION2 = """
579
575
  {
580
576
  "consumption": {"BEN_mcaid_value": 0.9}
581
577
  }
@@ -583,8 +579,8 @@ paramtools_revision2 = """
583
579
 
584
580
 
585
581
  @pytest.mark.parametrize("good_revision", [
586
- taxcalc_revision,
587
- paramtools_revision,
582
+ TAXCALC_REVISION,
583
+ PARAMTOOLS_REVISION,
588
584
  ])
589
585
  def test_read_json_revision(good_revision):
590
586
  """
@@ -593,7 +589,7 @@ def test_read_json_revision(good_revision):
593
589
  # pllint: disable=private-method
594
590
  with pytest.raises(TypeError):
595
591
  # error because first obj argument is neither None nor a string
596
- Parameters._read_json_revision(list(), '')
592
+ Parameters._read_json_revision([], '')
597
593
  with pytest.raises(ValueError):
598
594
  # error because second topkey argument must be a string
599
595
  Parameters._read_json_revision(good_revision, 999)
@@ -603,9 +599,9 @@ def test_read_json_revision(good_revision):
603
599
 
604
600
 
605
601
  @pytest.mark.parametrize("params,is_paramtools", [
606
- (taxcalc_revision, False),
607
- (paramtools_revision, True),
608
- (paramtools_revision2, True),
602
+ (TAXCALC_REVISION, False),
603
+ (PARAMTOOLS_REVISION, True),
604
+ (PARAMTOOLS_REVISION2, True),
609
605
  ])
610
606
  def test_read_json_revision_foramts(params, is_paramtools):
611
607
  """
@@ -13,8 +13,7 @@ import json
13
13
  import numpy as np
14
14
  import pytest
15
15
  import paramtools as pt
16
- # pylint: disable=import-error
17
- from taxcalc import Policy
16
+ from taxcalc.policy import Policy
18
17
 
19
18
 
20
19
  def cmp_policy_objs(pol1, pol2, year_range=None, exclude=None):
@@ -30,7 +29,7 @@ def cmp_policy_objs(pol1, pol2, year_range=None, exclude=None):
30
29
  else:
31
30
  pol1.clear_state()
32
31
  pol2.clear_state()
33
- for param in pol1._data:
32
+ for param in pol1._data: # pylint: disable=protected-access
34
33
  if exclude and param in exclude:
35
34
  continue
36
35
  v1 = getattr(pol1, param)
@@ -562,7 +561,7 @@ def test_order_of_indexing_and_level_reforms():
562
561
  make no difference to the post-reform policy parameter values.
563
562
  """
564
563
  # specify two reforms that raises the MTE and stops its indexing in 2015
565
- reform = [
564
+ reforms = [
566
565
  {
567
566
  'SS_Earnings_c': {2015: 500000},
568
567
  'SS_Earnings_c-indexed': {2015: False}
@@ -577,17 +576,17 @@ def test_order_of_indexing_and_level_reforms():
577
576
  ppo = [Policy(), Policy()]
578
577
  # apply reforms to corresponding Policy object & check post-reform values
579
578
  syr = Policy.JSON_START_YEAR
580
- for ref in range(len(reform)): # pylint: disable=consider-using-enumerate
579
+ for idx, reform in enumerate(reforms):
581
580
  # confirm pre-reform MTE values in 2014-2017
582
- mte = ppo[ref]._SS_Earnings_c
581
+ mte = ppo[idx]._SS_Earnings_c
583
582
  assert mte[2014 - syr] == 117000
584
583
  assert mte[2015 - syr] == 118500
585
584
  assert mte[2016 - syr] == 118500
586
585
  assert mte[2017 - syr] < 500000
587
586
  # implement reform in 2015
588
- ppo[ref].implement_reform(reform[ref])
587
+ ppo[idx].implement_reform(reform)
589
588
  # confirm post-reform MTE values in 2014-2017
590
- mte = ppo[ref]._SS_Earnings_c
589
+ mte = ppo[idx]._SS_Earnings_c
591
590
  assert mte[2014 - syr] == 117000
592
591
  assert mte[2015 - syr] == 500000
593
592
  assert mte[2016 - syr] == 500000
@@ -765,7 +764,7 @@ def test_section_titles(tests_path):
765
764
  md_text = md_file.read()
766
765
  md_dict = generate_section_dictionary(md_text)
767
766
  # ... make sure every md_dict section title is in valid_dict
768
- for sec1title,secdict in md_dict.items():
767
+ for sec1title, secdict in md_dict.items():
769
768
  assert isinstance(secdict, dict)
770
769
  assert sec1title in valid_dict
771
770
  for sec2title in secdict:
@@ -1332,7 +1331,7 @@ def test_multiple_cpi_swaps2():
1332
1331
  {"year": 2018, "value": 500000},
1333
1332
  ],
1334
1333
  "SS_Earnings_c-indexed": [
1335
- {"year": 2017, "value": False},
1334
+ {"year": 2017, "value": False},
1336
1335
  {"year": 2019, "value": True},
1337
1336
  ],
1338
1337
  "AMT_em-indexed": [
@@ -1417,6 +1416,9 @@ def test_multiple_cpi_swaps2():
1417
1416
  )
1418
1417
 
1419
1418
 
1419
+ # pylint: disable=invalid-name
1420
+
1421
+
1420
1422
  def test_adj_CPI_offset_and_index_status():
1421
1423
  """
1422
1424
  Test changing parameter_indexing_CPI_offset and another
@@ -1540,8 +1542,8 @@ def test_two_sets_of_tax_brackets():
1540
1542
  Test that II_brk? and PT_brk? values are the same under current law.
1541
1543
  """
1542
1544
  pol = Policy()
1543
- brackets = range(1, 7+1)
1544
- years = range(Policy.JSON_START_YEAR, Policy.LAST_KNOWN_YEAR+1)
1545
+ brackets = range(1, 7 + 1)
1546
+ years = range(Policy.JSON_START_YEAR, Policy.LAST_KNOWN_YEAR + 1)
1545
1547
  emsg = ''
1546
1548
  for year in years:
1547
1549
  pol.set_year(year)
@@ -11,8 +11,9 @@ import copy
11
11
  import numpy as np
12
12
  import pandas as pd
13
13
  import pytest
14
- # pylint: disable=import-error
15
- from taxcalc import Policy, Records, Calculator
14
+ from taxcalc.policy import Policy
15
+ from taxcalc.records import Records
16
+ from taxcalc.calculator import Calculator
16
17
 
17
18
 
18
19
  def create_base_table(test_path):
@@ -60,10 +61,10 @@ def create_base_table(test_path):
60
61
  read_vars = list(records_varinfo.USABLE_READ_VARS - unused_var_set)
61
62
  # get read variable information from JSON file
62
63
  rec_vars_path = os.path.join(test_path, '..', 'records_variables.json')
63
- with open(rec_vars_path) as rvfile:
64
+ with open(rec_vars_path, 'r', encoding='utf-8') as rvfile:
64
65
  read_var_dict = json.load(rvfile)
65
66
  # create table_dict with sorted read vars followed by sorted calc vars
66
- table_dict = dict()
67
+ table_dict = {}
67
68
  for var in sorted(read_vars):
68
69
  if "taxdata_puf" in read_var_dict['read'][var]['availability']:
69
70
  table_dict[var] = read_var_dict['read'][var]['desc']
@@ -85,14 +86,14 @@ def calculate_corr_stats(calc, table):
85
86
  errmsg = ''
86
87
  for varname1 in table.index:
87
88
  var1 = calc.array(varname1)
88
- var1_cc = list()
89
+ var1_cc = []
89
90
  for varname2 in table.index:
90
91
  var2 = calc.array(varname2)
91
92
  try:
92
93
  cor = np.corrcoef(var1, var2)[0][1]
93
94
  except FloatingPointError:
94
- msg = 'corr-coef error for {} and {}\n'
95
- errmsg += msg.format(varname1, varname2)
95
+ msg = f'corr-coef error for {varname1} and {varname2}\n'
96
+ errmsg += msg
96
97
  cor = 9.99 # because could not compute it
97
98
  var1_cc.append(cor)
98
99
  table[varname1] = var1_cc
@@ -105,7 +106,7 @@ def calculate_mean_stats(calc, table, year):
105
106
  Calculate weighted means for year.
106
107
  """
107
108
  total_weight = calc.total_weight()
108
- means = list()
109
+ means = []
109
110
  for varname in table.index:
110
111
  wmean = calc.weighted_total(varname) / total_weight
111
112
  means.append(wmean)
@@ -131,14 +132,13 @@ def differences(new_filename, old_filename, stat_kind):
131
132
  if diffs:
132
133
  new_name = os.path.basename(new_filename)
133
134
  old_name = os.path.basename(old_filename)
134
- msg = '{} RESULTS DIFFER:\n'.format(stat_kind)
135
+ msg = f'{stat_kind} RESULTS DIFFER:\n'
135
136
  msg += '-------------------------------------------------'
136
137
  msg += '-------------\n'
137
- msg += '--- NEW RESULTS IN {} FILE ---\n'.format(new_name)
138
- msg += '--- if new OK, copy {} to ---\n'.format(new_name)
139
- msg += '--- {} ---\n'.format(old_name)
140
- msg += '--- and rerun test. '
141
- msg += ' ---\n'
138
+ msg += f'--- NEW RESULTS IN {new_name} FILE ---\n'
139
+ msg += f'--- if new OK, copy {new_name} to\n'
140
+ msg += f'--- {old_name} \n'
141
+ msg += '--- and rerun test. '
142
142
  msg += '-------------------------------------------------'
143
143
  msg += '-------------\n'
144
144
  else:
@@ -8,8 +8,6 @@ and from the Census CPS file for the corresponding year. If you have
8
8
  acquired from IRS the most recent SOI PUF file and want to execute
9
9
  this program, contact the Tax-Calculator development team to discuss
10
10
  your options.
11
-
12
- Read Tax-Calculator/TESTING.md for details.
13
11
  """
14
12
  # CODING-STYLE CHECKS:
15
13
  # pycodestyle test_pufcsv.py
@@ -20,8 +18,9 @@ import json
20
18
  import pytest
21
19
  import numpy as np
22
20
  import pandas as pd
23
- # pylint: disable=import-error
24
- from taxcalc import Policy, Records, Calculator
21
+ from taxcalc.policy import Policy
22
+ from taxcalc.records import Records
23
+ from taxcalc.calculator import Calculator
25
24
 
26
25
 
27
26
  START_YEAR = 2017
@@ -57,7 +56,7 @@ def test_agg(tests_path, puf_fullsample):
57
56
  if not np.allclose(adt[icol].values, edt[str(icol)].values):
58
57
  diffs = True
59
58
  if diffs:
60
- new_filename = '{}{}'.format(aggres_path[:-10], 'actual.csv')
59
+ new_filename = f'{aggres_path[:-10]}actual.csv'
61
60
  adt.to_csv(new_filename, float_format='%.1f')
62
61
  msg = 'PUFCSV AGG RESULTS DIFFER FOR FULL-SAMPLE\n'
63
62
  msg += '-------------------------------------------------\n'
@@ -118,27 +117,32 @@ def mtr_bin_counts(mtr_data, bin_edges, recid):
118
117
  res = ''
119
118
  (bincount, _) = np.histogram(mtr_data.round(decimals=4), bins=bin_edges)
120
119
  sum_bincount = np.sum(bincount)
121
- res += '{} :'.format(sum_bincount)
120
+ res += f'{sum_bincount} :'
122
121
  for idx in range(len(bin_edges) - 1):
123
- res += ' {:6d}'.format(bincount[idx])
122
+ res += f' {bincount[idx]:6d}'
124
123
  res += '\n'
125
124
  if sum_bincount < mtr_data.size:
126
125
  res += 'WARNING: sum of bin counts is too low\n'
127
- recinfo = ' mtr={:.2f} for recid={}\n'
128
126
  mtr_min = mtr_data.min()
129
127
  mtr_max = mtr_data.max()
130
128
  bin_min = min(bin_edges)
131
129
  bin_max = max(bin_edges)
132
130
  if mtr_min < bin_min:
133
- res += ' min(mtr)={:.2f}\n'.format(mtr_min)
131
+ res += f' min(mtr)={mtr_min:.2f}\n'
134
132
  for idx in range(mtr_data.size):
135
133
  if mtr_data[idx] < bin_min:
136
- res += recinfo.format(mtr_data[idx], recid[idx])
134
+ res += (
135
+ f' mtr={mtr_data[idx]:.2f} '
136
+ f'for recid={recid[idx]}\n'
137
+ )
137
138
  if mtr_max > bin_max:
138
- res += ' max(mtr)={:.2f}\n'.format(mtr_max)
139
+ res += f' max(mtr)={mtr_max:.2f}\n'
139
140
  for idx in range(mtr_data.size):
140
141
  if mtr_data[idx] > bin_max:
141
- res += recinfo.format(mtr_data[idx], recid[idx])
142
+ res += (
143
+ f' mtr={mtr_data[idx]:.2f} '
144
+ f'for recid={recid[idx]}\n'
145
+ )
142
146
  return res
143
147
 
144
148
 
@@ -169,24 +173,20 @@ def nonsmall_diffs(linelist1, linelist2, small=0.0):
169
173
  for line1, line2 in zip(linelist1, linelist2):
170
174
  if line1 == line2:
171
175
  continue
172
- else:
173
- tokens1 = line1.replace(',', '').split()
174
- tokens2 = line2.replace(',', '').split()
175
- for tok1, tok2 in zip(tokens1, tokens2):
176
- tok1_isfloat = isfloat(tok1)
177
- tok2_isfloat = isfloat(tok2)
178
- if tok1_isfloat and tok2_isfloat:
179
- if abs(float(tok1) - float(tok2)) <= smallamt:
180
- continue
181
- else:
182
- return True
183
- elif not tok1_isfloat and not tok2_isfloat:
184
- if tok1 == tok2:
185
- continue
186
- else:
187
- return True
188
- else:
189
- return True
176
+ tokens1 = line1.replace(',', '').split()
177
+ tokens2 = line2.replace(',', '').split()
178
+ for tok1, tok2 in zip(tokens1, tokens2):
179
+ tok1_isfloat = isfloat(tok1)
180
+ tok2_isfloat = isfloat(tok2)
181
+ if tok1_isfloat and tok2_isfloat:
182
+ if abs(float(tok1) - float(tok2)) <= smallamt:
183
+ continue
184
+ return True
185
+ if not tok1_isfloat and not tok2_isfloat:
186
+ if tok1 == tok2:
187
+ continue
188
+ return True
189
+ return True
190
190
  return False
191
191
 
192
192
 
@@ -207,7 +207,7 @@ def test_mtr(tests_path, puf_path):
207
207
  res += 'MTR computed using NEGATIVE finite_diff '
208
208
  else:
209
209
  res += 'MTR computed using POSITIVE finite_diff '
210
- res += 'for tax year {}\n'.format(MTR_TAX_YEAR)
210
+ res += f'for tax year {MTR_TAX_YEAR}\n'
211
211
  # create a Policy object (clp) containing current-law policy parameters
212
212
  clp = Policy()
213
213
  clp.set_year(MTR_TAX_YEAR)
@@ -216,15 +216,15 @@ def test_mtr(tests_path, puf_path):
216
216
  recid = puf.RECID # pylint: disable=no-member
217
217
  # create a Calculator object using clp policy and puf records
218
218
  calc = Calculator(policy=clp, records=puf)
219
- res += '{} = {}\n'.format('Total number of data records', puf.array_length)
219
+ res += f'Total number of data records = {puf.array_length}\n'
220
220
  res += 'PTAX mtr histogram bin edges:\n'
221
- res += ' {}\n'.format(PTAX_MTR_BIN_EDGES)
221
+ res += f' {PTAX_MTR_BIN_EDGES}\n'
222
222
  res += 'ITAX mtr histogram bin edges:\n'
223
- res += ' {}\n'.format(ITAX_MTR_BIN_EDGES)
223
+ res += f' {ITAX_MTR_BIN_EDGES}\n'
224
224
  variable_header = 'PTAX and ITAX mtr histogram bin counts for'
225
225
  # compute marginal tax rate (mtr) histograms for each mtr variable
226
226
  for var_str in Calculator.MTR_VALID_VARIABLES:
227
- zero_out = (var_str == 'e01400')
227
+ zero_out = var_str == 'e01400'
228
228
  (mtr_ptax, mtr_itax, _) = calc.mtr(variable_str=var_str,
229
229
  negative_finite_diff=MTR_NEG_DIFF,
230
230
  zero_out_calculated_vars=zero_out,
@@ -250,17 +250,17 @@ def test_mtr(tests_path, puf_path):
250
250
  # only MARS==2 filing units have valid MTR values
251
251
  mtr_ptax = mtr_ptax[calc.array('MARS') == 2]
252
252
  mtr_itax = mtr_itax[calc.array('MARS') == 2]
253
- res += '{} {}:\n'.format(variable_header, var_str)
253
+ res += f'{variable_header} {var_str}:\n'
254
254
  res += mtr_bin_counts(mtr_ptax, PTAX_MTR_BIN_EDGES, recid)
255
255
  res += mtr_bin_counts(mtr_itax, ITAX_MTR_BIN_EDGES, recid)
256
256
  # check for differences between actual and expected results
257
257
  mtrres_path = os.path.join(tests_path, 'pufcsv_mtr_expect.txt')
258
- with open(mtrres_path, 'r') as expected_file:
258
+ with open(mtrres_path, 'r', encoding='utf-8') as expected_file:
259
259
  txt = expected_file.read()
260
260
  expected_results = txt.rstrip('\n\t ') + '\n' # cleanup end of file txt
261
261
  if nonsmall_diffs(res.splitlines(True), expected_results.splitlines(True)):
262
- new_filename = '{}{}'.format(mtrres_path[:-10], 'actual.txt')
263
- with open(new_filename, 'w') as new_file:
262
+ new_filename = f'{mtrres_path[:-10]}actual.txt'
263
+ with open(new_filename, 'w', encoding='utf-8') as new_file:
264
264
  new_file.write(res)
265
265
  msg = 'PUFCSV MTR RESULTS DIFFER\n'
266
266
  msg += '-------------------------------------------------\n'
@@ -372,7 +372,7 @@ def test_puf_availability(tests_path, puf_path):
372
372
  pufvars = set(list(pufdf))
373
373
  # make set of variable names that are marked as puf.csv available
374
374
  rvpath = os.path.join(tests_path, '..', 'records_variables.json')
375
- with open(rvpath, 'r') as rvfile:
375
+ with open(rvpath, 'r', encoding='utf-8') as rvfile:
376
376
  rvdict = json.load(rvfile)
377
377
  recvars = set()
378
378
  for vname, vdict in rvdict['read'].items():