taxcalc 4.4.0__py3-none-any.whl → 4.5.0__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 (57) 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.json +1 -1
  6. taxcalc/consumption.py +9 -4
  7. taxcalc/cps_weights.csv.gz +0 -0
  8. taxcalc/data.py +8 -8
  9. taxcalc/decorators.py +3 -3
  10. taxcalc/growdiff.json +1 -1
  11. taxcalc/growdiff.py +5 -0
  12. taxcalc/growfactors.csv +26 -25
  13. taxcalc/growfactors.py +1 -1
  14. taxcalc/parameters.py +85 -42
  15. taxcalc/policy.py +2 -2
  16. taxcalc/policy_current_law.json +87 -87
  17. taxcalc/puf_ratios.csv +15 -14
  18. taxcalc/puf_weights.csv.gz +0 -0
  19. taxcalc/records.py +1 -0
  20. taxcalc/records_variables.json +6 -0
  21. taxcalc/reforms/ext.json +21 -21
  22. taxcalc/taxcalcio.py +49 -44
  23. taxcalc/tests/cmpi_cps_expect.txt +6 -6
  24. taxcalc/tests/cmpi_puf_expect.txt +6 -6
  25. taxcalc/tests/conftest.py +43 -42
  26. taxcalc/tests/cpscsv_agg_expect.csv +22 -22
  27. taxcalc/tests/puf_var_wght_means_by_year.csv +70 -70
  28. taxcalc/tests/pufcsv_agg_expect.csv +22 -22
  29. taxcalc/tests/test_4package.py +9 -7
  30. taxcalc/tests/test_benefits.py +9 -8
  31. taxcalc/tests/test_calcfunctions.py +55 -38
  32. taxcalc/tests/test_calculator.py +11 -6
  33. taxcalc/tests/test_compare.py +45 -51
  34. taxcalc/tests/test_compatible_data.py +9 -7
  35. taxcalc/tests/test_consumption.py +38 -18
  36. taxcalc/tests/test_cpscsv.py +33 -31
  37. taxcalc/tests/test_data.py +31 -24
  38. taxcalc/tests/test_decorators.py +84 -32
  39. taxcalc/tests/test_growdiff.py +16 -13
  40. taxcalc/tests/test_growfactors.py +8 -8
  41. taxcalc/tests/test_parameters.py +55 -59
  42. taxcalc/tests/test_policy.py +14 -12
  43. taxcalc/tests/test_puf_var_stats.py +14 -14
  44. taxcalc/tests/test_pufcsv.py +40 -40
  45. taxcalc/tests/test_records.py +73 -60
  46. taxcalc/tests/test_reforms.py +35 -32
  47. taxcalc/tests/test_responses.py +4 -4
  48. taxcalc/tests/test_taxcalcio.py +76 -62
  49. taxcalc/tests/test_utils.py +78 -46
  50. taxcalc/utils.py +49 -42
  51. taxcalc/validation/taxsim35/taxsim_emulation.json +1 -5
  52. {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/METADATA +19 -5
  53. {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/RECORD +57 -57
  54. {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/WHEEL +1 -1
  55. {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/LICENSE +0 -0
  56. {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/entry_points.txt +0 -0
  57. {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/top_level.txt +0 -0
taxcalc/reforms/ext.json CHANGED
@@ -1,32 +1,32 @@
1
1
  // REFORM TO EXTEND TEMPORARY TCJA PROVISIONS BEYOND 2025
2
- // USING TAX-CALCULATOR 4.4.0
3
- // WITH 2025-to-2026 INDEXING FACTOR = 1.024900
4
- // AND 2028-to-2029 INDEXING FACTOR = 1.021800
2
+ // USING TAX-CALCULATOR 4.4.1
3
+ // WITH 2025-to-2026 INDEXING FACTOR = 1.022300
4
+ // AND 2028-to-2029 INDEXING FACTOR = 1.022600
5
5
  {
6
6
  "II_rt1": {"2026": 0.10},
7
- "II_brk1": {"2026": [12221.93, 24443.86, 12221.93, 17423.3, 24443.86]},
7
+ "II_brk1": {"2026": [12190.93, 24381.86, 12190.93, 17379.1, 24381.86]},
8
8
  "PT_rt1": {"2026": 0.10},
9
- "PT_brk1": {"2026": [12221.93, 24443.86, 12221.93, 17423.3, 24443.86]},
9
+ "PT_brk1": {"2026": [12190.93, 24381.86, 12190.93, 17379.1, 24381.86]},
10
10
  "II_rt2": {"2026": 0.12},
11
- "II_brk2": {"2026": [49682.03, 99364.06, 49682.03, 66464.76, 99364.06]},
11
+ "II_brk2": {"2026": [49555.99, 99111.98, 49555.99, 66296.16, 99111.98]},
12
12
  "PT_rt2": {"2026": 0.12},
13
- "PT_brk2": {"2026": [49682.03, 99364.06, 49682.03, 66464.76, 99364.06]},
13
+ "PT_brk2": {"2026": [49555.99, 99111.98, 49555.99, 66296.16, 99111.98]},
14
14
  "II_rt3": {"2026": 0.22},
15
- "II_brk3": {"2026": [105923.42, 211846.83, 105923.42, 105923.42, 211846.83]},
15
+ "II_brk3": {"2026": [105654.7, 211309.41, 105654.7, 105654.7, 211309.41]},
16
16
  "PT_rt3": {"2026": 0.22},
17
- "PT_brk3": {"2026": [105923.42, 211846.83, 105923.42, 105923.42, 211846.83]},
17
+ "PT_brk3": {"2026": [105654.7, 211309.41, 105654.7, 105654.7, 211309.41]},
18
18
  "II_rt4": {"2026": 0.24},
19
- "II_brk4": {"2026": [202212.77, 404425.54, 202212.77, 202212.77, 404425.54]},
19
+ "II_brk4": {"2026": [201699.79, 403399.58, 201699.79, 201699.79, 403399.58]},
20
20
  "PT_rt4": {"2026": 0.24},
21
- "PT_brk4": {"2026": [202212.77, 404425.54, 202212.77, 202212.77, 404425.54]},
21
+ "PT_brk4": {"2026": [201699.79, 403399.58, 201699.79, 201699.79, 403399.58]},
22
22
  "II_rt5": {"2026": 0.32},
23
- "II_brk5": {"2026": [256763.07, 513526.14, 256763.07, 256737.45, 513526.14]},
23
+ "II_brk5": {"2026": [256111.71, 512223.42, 256111.71, 256086.15, 512223.42]},
24
24
  "PT_rt5": {"2026": 0.32},
25
- "PT_brk5": {"2026": [256763.07, 513526.14, 256763.07, 256737.45, 513526.14]},
25
+ "PT_brk5": {"2026": [256111.71, 512223.42, 256111.71, 256086.15, 512223.42]},
26
26
  "II_rt6": {"2026": 0.35},
27
- "II_brk6": {"2026": [641946.12, 770314.84, 385157.42, 641946.12, 770314.84]},
27
+ "II_brk6": {"2026": [640317.6, 768360.68, 384180.34, 640317.6, 768360.68]},
28
28
  "PT_rt6": {"2026": 0.35},
29
- "PT_brk6": {"2026": [641946.12, 770314.84, 385157.42, 641946.12, 770314.84]},
29
+ "PT_brk6": {"2026": [640317.6, 768360.68, 384180.34, 640317.6, 768360.68]},
30
30
  "II_rt7": {"2026": 0.37},
31
31
  "II_brk7": {"2026": [9e+99, 9e+99, 9e+99, 9e+99, 9e+99]},
32
32
  "PT_rt7": {"2026": 0.37},
@@ -36,10 +36,10 @@
36
36
  "ODC_c": {"2026": 500.00},
37
37
  "CTC_ps": {"2026": [200000.0, 400000.0, 200000.0, 200000.0, 400000.0]},
38
38
  "ACTC_Income_thd": {"2026": 2500.00},
39
- "AMT_em": {"2026": [90293.69, 140411.3, 70205.65, 90293.69, 140411.3]},
40
- "AMT_em_ps": {"2026": [641946.12, 1283892.23, 641946.12, 641946.12, 1283892.23]},
41
- "AMT_em_pe": {"2026": 945745.66},
42
- "STD": {"2026": [15373.5, 30747.0, 15373.5, 23060.25, 30747.0]},
39
+ "AMT_em": {"2026": [90064.63, 140055.1, 70027.55, 90064.63, 140055.1]},
40
+ "AMT_em_ps": {"2026": [640317.6, 1280635.21, 640317.6, 640317.6, 1280635.21]},
41
+ "AMT_em_pe": {"2026": 940953.35},
42
+ "STD": {"2026": [15334.5, 30669.0, 15334.5, 23001.75, 30669.0]},
43
43
  "ID_AllTaxes_c": {"2026": [10000.0, 10000.0, 5000.0, 10000.0, 10000.0]},
44
44
  "ID_Charity_crt_cash": {"2026": 0.60},
45
45
  "ID_Casualty_hc": {"2026": 1.00},
@@ -50,10 +50,10 @@
50
50
  "II_em": {"2026": 0.00},
51
51
  "II_em_ps": {"2026": [9e+99, 9e+99, 9e+99, 9e+99, 9e+99]},
52
52
  "PT_qbid_rt": {"2026": 0.20},
53
- "PT_qbid_taxinc_thd": {"2026": [202212.77, 404425.54, 202212.77, 202212.77, 202212.77]},
53
+ "PT_qbid_taxinc_thd": {"2026": [201699.79, 403399.58, 201699.79, 201699.79, 201699.79]},
54
54
  "PT_qbid_taxinc_gap": {"2026": [50000.0, 100000.0, 50000.0, 50000.0, 100000.0]},
55
55
  "PT_qbid_w2_wages_rt": {"2026": 0.50},
56
56
  "PT_qbid_alt_w2_wages_rt": {"2026": 0.25},
57
57
  "PT_qbid_alt_property_rt": {"2026": 0.03},
58
- "ALD_BusinessLosses_c": {"2029": [342803.73, 685607.47, 342803.73, 342803.73, 685607.47]}
58
+ "ALD_BusinessLosses_c": {"2029": [342904.94, 685809.88, 342904.94, 342904.94, 685809.88]}
59
59
  }
taxcalc/taxcalcio.py CHANGED
@@ -66,8 +66,8 @@ class TaxCalcIO():
66
66
 
67
67
  def __init__(self, input_data, tax_year, baseline, reform, assump,
68
68
  outdir=None):
69
- # pylint: disable=too-many-arguments,too-many-locals
70
- # pylint: disable=too-many-branches,too-many-statements
69
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
70
+ # pylint: disable=too-many-branches,too-many-statements,too-many-locals
71
71
  self.gf_reform = None
72
72
  self.errmsg = ''
73
73
  # check name and existence of INPUT file
@@ -82,17 +82,17 @@ class TaxCalcIO():
82
82
  fname = os.path.basename(input_data)
83
83
  # check if fname ends with ".csv"
84
84
  if fname.endswith('.csv'):
85
- inp = '{}-{}'.format(fname[:-4], str(tax_year)[2:])
85
+ inp = f'{fname[:-4]}-{str(tax_year)[2:]}'
86
86
  else:
87
87
  msg = 'INPUT file name does not end in .csv'
88
- self.errmsg += 'ERROR: {}\n'.format(msg)
88
+ self.errmsg += f'ERROR: {msg}\n'
89
89
  # check existence of INPUT file
90
90
  self.puf_input_data = input_data.endswith('puf.csv')
91
91
  self.cps_input_data = input_data.endswith('cps.csv')
92
92
  self.tmd_input_data = input_data.endswith('tmd.csv')
93
93
  if not self.cps_input_data and not os.path.isfile(input_data):
94
94
  msg = 'INPUT file could not be found'
95
- self.errmsg += 'ERROR: {}\n'.format(msg)
95
+ self.errmsg += f'ERROR: {msg}\n'
96
96
  # if tmd_input_data is True, construct weights and gfactor paths
97
97
  if self.tmd_input_data: # pragma: no cover
98
98
  tmd_dir = os.path.dirname(input_data)
@@ -106,15 +106,15 @@ class TaxCalcIO():
106
106
  self.tmd_gfactor = os.path.join(tmd_dir, 'tmd_growfactors.csv')
107
107
  if not os.path.isfile(self.tmd_weights):
108
108
  msg = f'weights file {self.tmd_weights} could not be found'
109
- self.errmsg += 'ERROR: {}\n'.format(msg)
109
+ self.errmsg += f'ERROR: {msg}\n'
110
110
  if not os.path.isfile(self.tmd_gfactor):
111
111
  msg = f'gfactor file {self.tmd_gfactor} could not be found'
112
- self.errmsg += 'ERROR: {}\n'.format(msg)
112
+ self.errmsg += f'ERROR: {msg}\n'
113
113
  elif isinstance(input_data, pd.DataFrame):
114
- inp = 'df-{}'.format(str(tax_year)[2:])
114
+ inp = f'df-{str(tax_year)[2:]}'
115
115
  else:
116
116
  msg = 'INPUT is neither string nor Pandas DataFrame'
117
- self.errmsg += 'ERROR: {}\n'.format(msg)
117
+ self.errmsg += f'ERROR: {msg}\n'
118
118
  # check name and existence of BASELINE file
119
119
  bas = '-x'
120
120
  if baseline is None:
@@ -124,17 +124,17 @@ class TaxCalcIO():
124
124
  fname = os.path.basename(baseline)
125
125
  # check if fname ends with ".json"
126
126
  if fname.endswith('.json'):
127
- bas = '-{}'.format(fname[:-5])
127
+ bas = f'-{fname[:-5]}'
128
128
  else:
129
129
  msg = 'BASELINE file name does not end in .json'
130
- self.errmsg += 'ERROR: {}\n'.format(msg)
130
+ self.errmsg += f'ERROR: {msg}\n'
131
131
  # check existence of BASELINE file
132
132
  if not os.path.isfile(baseline):
133
133
  msg = 'BASELINE file could not be found'
134
- self.errmsg += 'ERROR: {}\n'.format(msg)
134
+ self.errmsg += f'ERROR: {msg}\n'
135
135
  else:
136
136
  msg = 'TaxCalcIO.ctor: baseline is neither None nor str'
137
- self.errmsg += 'ERROR: {}\n'.format(msg)
137
+ self.errmsg += f'ERROR: {msg}\n'
138
138
  # check name(s) and existence of REFORM file(s)
139
139
  ref = '-x'
140
140
  if reform is None:
@@ -150,12 +150,12 @@ class TaxCalcIO():
150
150
  fname = os.path.basename(rfm)
151
151
  # check if fname ends with ".json"
152
152
  if not fname.endswith('.json'):
153
- msg = '{} does not end in .json'.format(fname)
154
- self.errmsg += 'ERROR: REFORM file name {}\n'.format(msg)
153
+ msg = f'{fname} does not end in .json'
154
+ self.errmsg += f'ERROR: REFORM file name {msg}\n'
155
155
  # check existence of REFORM file
156
156
  if not os.path.isfile(rfm):
157
- msg = '{} could not be found'.format(rfm)
158
- self.errmsg += 'ERROR: REFORM file {}\n'.format(msg)
157
+ msg = f'{rfm} could not be found'
158
+ self.errmsg += f'ERROR: REFORM file {msg}\n'
159
159
  # add fname to list of refnames used in output file names
160
160
  refnames.append(fname)
161
161
  # create (possibly compound) reform name for output file names
@@ -165,10 +165,10 @@ class TaxCalcIO():
165
165
  num_refnames += 1
166
166
  if num_refnames > 1:
167
167
  ref += '+'
168
- ref += '{}'.format(refname[:-5])
168
+ ref += f'{refname[:-5]}'
169
169
  else:
170
170
  msg = 'TaxCalcIO.ctor: reform is neither None nor str'
171
- self.errmsg += 'ERROR: {}\n'.format(msg)
171
+ self.errmsg += f'ERROR: {msg}\n'
172
172
  # check name and existence of ASSUMP file
173
173
  asm = '-x'
174
174
  if assump is None:
@@ -178,17 +178,17 @@ class TaxCalcIO():
178
178
  fname = os.path.basename(assump)
179
179
  # check if fname ends with ".json"
180
180
  if fname.endswith('.json'):
181
- asm = '-{}'.format(fname[:-5])
181
+ asm = f'-{fname[:-5]}'
182
182
  else:
183
183
  msg = 'ASSUMP file name does not end in .json'
184
- self.errmsg += 'ERROR: {}\n'.format(msg)
184
+ self.errmsg += f'ERROR: {msg}\n'
185
185
  # check existence of ASSUMP file
186
186
  if not os.path.isfile(assump):
187
187
  msg = 'ASSUMP file could not be found'
188
- self.errmsg += 'ERROR: {}\n'.format(msg)
188
+ self.errmsg += f'ERROR: {msg}\n'
189
189
  else:
190
190
  msg = 'TaxCalcIO.ctor: assump is neither None nor str'
191
- self.errmsg += 'ERROR: {}\n'.format(msg)
191
+ self.errmsg += f'ERROR: {msg}\n'
192
192
  # check name and existence of OUTDIR
193
193
  if outdir is None:
194
194
  valid_outdir = True
@@ -199,13 +199,13 @@ class TaxCalcIO():
199
199
  else:
200
200
  valid_outdir = False
201
201
  msg = 'OUTDIR could not be found'
202
- self.errmsg += 'ERROR: {}\n'.format(msg)
202
+ self.errmsg += f'ERROR: {msg}\n'
203
203
  else:
204
204
  valid_outdir = False
205
205
  msg = 'TaxCalcIO.ctor: outdir is neither None nor str'
206
- self.errmsg += 'ERROR: {}\n'.format(msg)
206
+ self.errmsg += f'ERROR: {msg}\n'
207
207
  # create OUTPUT file name and delete any existing output files
208
- output_filename = '{}{}{}{}.csv'.format(inp, bas, ref, asm)
208
+ output_filename = f'{inp}{bas}{ref}{asm}.csv'
209
209
  if outdir is None:
210
210
  self._output_filename = output_filename
211
211
  delete_old_files = True
@@ -246,8 +246,8 @@ class TaxCalcIO():
246
246
  specifies whether or not exact tax calculations are done without
247
247
  any smoothing of "stair-step" provisions in the tax law.
248
248
  """
249
- # pylint: disable=too-many-arguments,too-many-locals
250
- # pylint: disable=too-many-statements,too-many-branches
249
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
250
+ # pylint: disable=too-many-statements,too-many-branches,too-many-locals
251
251
  self.errmsg = ''
252
252
  # instantiate base and reform GrowFactors objects
253
253
  if self.tmd_input_data:
@@ -300,7 +300,7 @@ class TaxCalcIO():
300
300
  try:
301
301
  gdiff_baseline.update_growdiff(paramdict['growdiff_baseline'])
302
302
  except paramtools.ValidationError as valerr_msg:
303
- self.errmsg += valerr_msg.__str__()
303
+ self.errmsg += str(valerr_msg)
304
304
  # apply gdiff_baseline to gfactor_base
305
305
  gdiff_baseline.apply_to(gfactors_base)
306
306
  # specify gdiff_response object
@@ -308,7 +308,7 @@ class TaxCalcIO():
308
308
  try:
309
309
  gdiff_response.update_growdiff(paramdict['growdiff_response'])
310
310
  except paramtools.ValidationError as valerr_msg:
311
- self.errmsg += valerr_msg.__str__()
311
+ self.errmsg += str(valerr_msg)
312
312
  # apply gdiff_baseline and gdiff_response to gfactor_ref
313
313
  gdiff_baseline.apply_to(gfactors_ref)
314
314
  gdiff_response.apply_to(gfactors_ref)
@@ -323,7 +323,7 @@ class TaxCalcIO():
323
323
  for _, errors in base.parameter_errors.items():
324
324
  self.errmsg += "\n".join(errors)
325
325
  except paramtools.ValidationError as valerr_msg:
326
- self.errmsg += valerr_msg.__str__()
326
+ self.errmsg += str(valerr_msg)
327
327
  # ... the reform Policy object
328
328
  if self.specified_reform:
329
329
  pol = Policy(gfactors=gfactors_ref, last_budget_year=last_b_year)
@@ -337,7 +337,7 @@ class TaxCalcIO():
337
337
  for _, errors in pol.parameter_errors.items():
338
338
  self.errmsg += "\n".join(errors)
339
339
  except paramtools.ValidationError as valerr_msg:
340
- self.errmsg += valerr_msg.__str__()
340
+ self.errmsg += str(valerr_msg)
341
341
  else:
342
342
  pol = Policy(gfactors=gfactors_base, last_budget_year=last_b_year)
343
343
  # create Consumption object
@@ -345,7 +345,7 @@ class TaxCalcIO():
345
345
  try:
346
346
  con.update_consumption(paramdict['consumption'])
347
347
  except paramtools.ValidationError as valerr_msg:
348
- self.errmsg += valerr_msg.__str__()
348
+ self.errmsg += str(valerr_msg)
349
349
  # any errors imply cannot proceed with calculations
350
350
  if self.errmsg:
351
351
  return
@@ -431,9 +431,8 @@ class TaxCalcIO():
431
431
  valid_set = recs_vinfo.USABLE_READ_VARS | recs_vinfo.CALCULATED_VARS
432
432
  for var in dump_vars_list:
433
433
  if var not in valid_set:
434
- msg = 'invalid variable name in tcdumpvars file: {}'
435
- msg = msg.format(var)
436
- self.errmsg += 'ERROR: {}\n'.format(msg)
434
+ msg = f'invalid variable name in tcdumpvars file: {var}'
435
+ self.errmsg += f'ERROR: {msg}\n'
437
436
  # add essential variables even if not on custom list
438
437
  if 'RECID' not in dump_vars_list:
439
438
  dump_vars_list.append('RECID')
@@ -494,7 +493,8 @@ class TaxCalcIO():
494
493
  -------
495
494
  Nothing
496
495
  """
497
- # pylint: disable=too-many-arguments,too-many-branches,too-many-locals
496
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
497
+ # pylint: disable=too-many-branches,too-many-locals
498
498
  if self.puf_input_data and self.calc.reform_warnings:
499
499
  warn = 'PARAMETER VALUE WARNING(S): {}\n{}{}' # pragma: no cover
500
500
  print( # pragma: no cover
@@ -599,6 +599,7 @@ class TaxCalcIO():
599
599
  """
600
600
  Write dump output to SQLite3 database table dump.
601
601
  """
602
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
602
603
  db_fname = self._output_filename.replace('.csv', '.db')
603
604
  dbcon = sqlite3.connect(db_fname)
604
605
  # write baseline table
@@ -685,8 +686,9 @@ class TaxCalcIO():
685
686
  weighted_sum, 'combined', include_groups=False
686
687
  ).values[:, 1]
687
688
  # write decile table to text file
688
- row = 'Weighted Tax {} by Baseline Expanded-Income Decile\n'
689
- tfile.write(row.format(tkind))
689
+ row = f'Weighted Tax {tkind} by Baseline Expanded-Income Decile\n'
690
+ tfile.write(row)
691
+ # pylint: disable=consider-using-f-string
690
692
  rowfmt = '{}{}{}{}{}{}\n'
691
693
  row = rowfmt.format(' Returns',
692
694
  ' ExpInc',
@@ -704,7 +706,7 @@ class TaxCalcIO():
704
706
  tfile.write(row)
705
707
  rowfmt = '{:9.2f}{:10.1f}{:10.1f}{:10.1f}{:10.1f}{:10.1f}\n'
706
708
  for decile in range(0, 10):
707
- row = '{:2d}'.format(decile)
709
+ row = f'{decile:2d}'
708
710
  row += rowfmt.format(rtns_series[decile] * 1e-6,
709
711
  xinc_series[decile] * 1e-9,
710
712
  itax_series[decile] * 1e-9,
@@ -720,6 +722,7 @@ class TaxCalcIO():
720
722
  htax_series.sum() * 1e-9,
721
723
  ctax_series.sum() * 1e-9)
722
724
  tfile.write(row)
725
+ # pylint: enable=consider-using-f-string
723
726
  del gdfx
724
727
  del rtns_series
725
728
  del xinc_series
@@ -776,10 +779,12 @@ class TaxCalcIO():
776
779
  """
777
780
  Write HTML graph file with title but no graph for specified reason.
778
781
  """
779
- txt = ('<html>\n'
780
- '<head><title>{}</title></head>\n'
781
- '<body><center<h1>{}</h1></center></body>\n'
782
- '</html>\n').format(title, reason)
782
+ txt = (
783
+ '<html>\n'
784
+ f'<head><title>{title}</title></head>\n'
785
+ f'<body><center<h1>{reason}</h1></center></body>\n'
786
+ '</html>\n'
787
+ )
783
788
  with open(fname, 'w', encoding='utf-8') as gfile:
784
789
  gfile.write(txt)
785
790
 
@@ -1,5 +1,5 @@
1
1
  TABLE for EITC
2
- AGI category T-C SOI %diff
2
+ AGIcategory T-C SOI %diff
3
3
  [-9e+99, 1) 0.014 0.210 -93.2
4
4
  [1, 5000) 1.555 1.184 +31.3
5
5
  [5000, 10000) 6.132 7.156 -14.3
@@ -21,7 +21,7 @@ AGI category T-C SOI %diff
21
21
  [10000000, 9e+99) 0.000 0.000 +nan
22
22
  ALL 54.894 68.525 -19.9
23
23
  TABLE for FCTC
24
- AGI category T-C SOI %diff
24
+ AGIcategory T-C SOI %diff
25
25
  [-9e+99, 1) 0.005 0.130 -96.0
26
26
  [1, 5000) 0.083 0.079 +4.4
27
27
  [5000, 10000) 1.118 1.474 -24.2
@@ -43,7 +43,7 @@ AGI category T-C SOI %diff
43
43
  [10000000, 9e+99) 0.000 0.000 +nan
44
44
  ALL 56.073 53.690 +4.4
45
45
  TABLE for NIIT
46
- AGI category T-C SOI %diff
46
+ AGIcategory T-C SOI %diff
47
47
  [-9e+99, 1) 0.000 0.000 -100.0
48
48
  [1, 5000) 0.000 0.000 +nan
49
49
  [5000, 10000) 0.000 0.000 +nan
@@ -65,7 +65,7 @@ AGI category T-C SOI %diff
65
65
  [10000000, 9e+99) 4.187 8.273 -49.4
66
66
  ALL 6.179 22.043 -72.0
67
67
  TABLE for ITAX
68
- AGI category T-C SOI %diff
68
+ AGIcategory T-C SOI %diff
69
69
  [-9e+99, 1) -0.006 0.242 -102.3
70
70
  [1, 5000) -1.529 0.041 -3838.6
71
71
  [5000, 10000) -6.862 0.368 -1964.8
@@ -87,7 +87,7 @@ AGI category T-C SOI %diff
87
87
  [10000000, 9e+99) 49.547 139.611 -64.5
88
88
  ALL 969.859 1457.891 -33.5
89
89
  TABLE for SETAX
90
- AGI category T-C SOI %diff
90
+ AGIcategory T-C SOI %diff
91
91
  [-9e+99, 1) 0.014 0.656 -97.9
92
92
  [1, 5000) 0.101 0.555 -81.8
93
93
  [5000, 10000) 0.319 1.896 -83.2
@@ -109,7 +109,7 @@ AGI category T-C SOI %diff
109
109
  [10000000, 9e+99) 0.073 0.638 -88.6
110
110
  ALL 48.503 60.174 -19.4
111
111
  TABLE for AMTAX
112
- AGI category T-C SOI %diff
112
+ AGIcategory T-C SOI %diff
113
113
  [-9e+99, 1) 0.000 0.022 -100.0
114
114
  [1, 5000) 0.000 0.000 -100.0
115
115
  [5000, 10000) 0.000 0.000 +nan
@@ -1,5 +1,5 @@
1
1
  TABLE for EITC
2
- AGI category T-C SOI %diff
2
+ AGIcategory T-C SOI %diff
3
3
  [-9e+99, 1) 0.174 0.210 -17.5
4
4
  [1, 5000) 1.495 1.184 +26.2
5
5
  [5000, 10000) 5.880 7.156 -17.8
@@ -21,7 +21,7 @@ AGI category T-C SOI %diff
21
21
  [10000000, 9e+99) 0.000 0.000 +nan
22
22
  ALL 68.057 68.525 -0.7
23
23
  TABLE for FCTC
24
- AGI category T-C SOI %diff
24
+ AGIcategory T-C SOI %diff
25
25
  [-9e+99, 1) 0.145 0.130 +11.2
26
26
  [1, 5000) 0.115 0.079 +44.7
27
27
  [5000, 10000) 1.084 1.474 -26.5
@@ -43,7 +43,7 @@ AGI category T-C SOI %diff
43
43
  [10000000, 9e+99) 0.000 0.000 +nan
44
44
  ALL 53.276 53.690 -0.8
45
45
  TABLE for NIIT
46
- AGI category T-C SOI %diff
46
+ AGIcategory T-C SOI %diff
47
47
  [-9e+99, 1) 0.000 0.000 -100.0
48
48
  [1, 5000) 0.000 0.000 +nan
49
49
  [5000, 10000) 0.000 0.000 +nan
@@ -65,7 +65,7 @@ AGI category T-C SOI %diff
65
65
  [10000000, 9e+99) 8.112 8.273 -1.9
66
66
  ALL 27.149 22.043 +23.2
67
67
  TABLE for ITAX
68
- AGI category T-C SOI %diff
68
+ AGIcategory T-C SOI %diff
69
69
  [-9e+99, 1) 4.502 0.242 +1756.7
70
70
  [1, 5000) -1.776 0.041 -4442.6
71
71
  [5000, 10000) -5.779 0.368 -1670.5
@@ -87,7 +87,7 @@ AGI category T-C SOI %diff
87
87
  [10000000, 9e+99) 97.259 139.611 -30.3
88
88
  ALL 1462.385 1457.891 +0.3
89
89
  TABLE for SETAX
90
- AGI category T-C SOI %diff
90
+ AGIcategory T-C SOI %diff
91
91
  [-9e+99, 1) 0.761 0.656 +16.1
92
92
  [1, 5000) 0.626 0.555 +12.7
93
93
  [5000, 10000) 1.633 1.896 -13.8
@@ -109,7 +109,7 @@ AGI category T-C SOI %diff
109
109
  [10000000, 9e+99) 0.033 0.638 -94.8
110
110
  ALL 49.779 60.174 -17.3
111
111
  TABLE for AMTAX
112
- AGI category T-C SOI %diff
112
+ AGIcategory T-C SOI %diff
113
113
  [-9e+99, 1) 0.012 0.022 -45.5
114
114
  [1, 5000) 0.000 0.000 -68.2
115
115
  [5000, 10000) 0.000 0.000 +nan
taxcalc/tests/conftest.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ The pytest configuration file.
3
+ """
4
+
1
5
  import os
2
6
  import time
3
7
  import glob
@@ -12,63 +16,61 @@ numpy.seterr(all='raise')
12
16
 
13
17
  @pytest.fixture
14
18
  def skip_jit(monkeypatch):
19
+ """Fixture docstring"""
15
20
  monkeypatch.setenv("TESTING", "True")
16
21
  yield
17
22
 
18
23
 
19
- @pytest.fixture(scope='session')
20
- def tests_path():
24
+ @pytest.fixture(scope='session', name='tests_path')
25
+ def tests_path_fixture():
26
+ """Fixture docstring"""
21
27
  return os.path.abspath(os.path.dirname(__file__))
22
28
 
23
29
 
24
- @pytest.fixture(scope='session')
25
- def cps_path(tests_path):
30
+ @pytest.fixture(scope='session', name='cps_path')
31
+ def cps_path_fixture(tests_path):
32
+ """Fixture docstring"""
26
33
  return os.path.join(tests_path, '..', 'cps.csv.gz')
27
34
 
28
35
 
29
- @pytest.fixture(scope='session')
30
- def cps_fullsample(cps_path):
36
+ @pytest.fixture(scope='session', name='cps_fullsample')
37
+ def cps_fullsample_fixture(cps_path):
38
+ """Fixture docstring"""
31
39
  return pandas.read_csv(cps_path)
32
40
 
33
41
 
34
42
  @pytest.fixture(scope='session')
35
43
  def cps_subsample(cps_fullsample):
44
+ """Fixture docstring"""
36
45
  # draw smaller cps.csv subsample than in test_cpscsv.py
37
46
  return cps_fullsample.sample(frac=0.01, random_state=123456789)
38
47
 
39
48
 
40
- @pytest.fixture(scope='session')
41
- def puf_path(tests_path):
49
+ @pytest.fixture(scope='session', name='puf_path')
50
+ def puf_path_fixture(tests_path):
51
+ """Fixture docstring"""
42
52
  return os.path.join(tests_path, '..', '..', 'puf.csv')
43
53
 
44
54
 
45
- @pytest.fixture(scope='session')
46
- def puf_fullsample(puf_path):
55
+ @pytest.fixture(scope='session', name='puf_fullsample')
56
+ def puf_fullsample_fixture(puf_path):
57
+ """Fixture docstring"""
47
58
  return pandas.read_csv(puf_path)
48
59
 
49
60
 
50
61
  @pytest.fixture(scope='session')
51
62
  def puf_subsample(puf_fullsample):
63
+ """Fixture docstring"""
52
64
  # draw same puf.csv subsample as in test_pufcsv.py
53
65
  return puf_fullsample.sample(frac=0.05, random_state=2222)
54
66
 
55
67
 
56
- @pytest.fixture(scope='session')
57
- def tmd_path(tests_path):
58
- return os.path.join(tests_path, '..', '..', 'tmd.csv')
59
-
60
-
61
- @pytest.fixture(scope='session')
62
- def tmd_fullsample(tmd_path):
63
- return pandas.read_csv(tmd_path)
64
-
65
-
66
68
  @pytest.fixture(scope='session', name='test_reforms_init')
67
69
  def fixture_test_reforms(tests_path):
68
70
  """
69
71
  Execute logic only once rather than on each pytest-xdist node.
70
72
  """
71
- # pylint: disable=too-many-locals
73
+ # pylint: disable=too-many-locals,too-many-statements
72
74
  num_reforms = 64 # must be same as NUM_REFORMS in test_reforms.py
73
75
  handling_logic = ('PYTEST_XDIST_WORKER' not in os.environ or
74
76
  os.environ['PYTEST_XDIST_WORKER'] == 'gw0')
@@ -76,7 +78,7 @@ def fixture_test_reforms(tests_path):
76
78
  actfile_path = os.path.join(tests_path, 'reforms_actual.csv')
77
79
  afiles = os.path.join(tests_path, 'reform_actual_*.csv')
78
80
  wait_secs = 1
79
- max_waits = 180
81
+ max_waits = 240
80
82
  # test_reforms setup
81
83
  if handling_logic:
82
84
  # remove reforms_actual.csv file if exists
@@ -86,7 +88,7 @@ def fixture_test_reforms(tests_path):
86
88
  for afile in glob.glob(afiles):
87
89
  os.remove(afile)
88
90
  # create reforms_actual_init file
89
- with open(initfile, 'w') as ifile:
91
+ with open(initfile, 'w', encoding='utf-8') as ifile:
90
92
  ifile.write('test_reforms initialization done')
91
93
  else:
92
94
  num_waits = 0
@@ -107,26 +109,26 @@ def fixture_test_reforms(tests_path):
107
109
  # compare actual and expected results for each test
108
110
  # ... read expected results
109
111
  efile_path = os.path.join(tests_path, 'reforms_expect.csv')
110
- with open(efile_path, 'r') as efile:
112
+ with open(efile_path, 'r', encoding='utf-8') as efile:
111
113
  expect_lines = efile.readlines()
112
114
  # ... compare actual and expected results for each test
113
115
  diffs = False
114
- actfile = open(actfile_path, 'w')
115
- actfile.write('rid,res1,res2,res3,res4\n')
116
- idx = 1 # expect_lines list index
117
- for rnum in range(1, num_reforms + 1):
118
- afile_path = os.path.join(tests_path,
119
- 'reform_actual_{}.csv'.format(rnum))
120
- with open(afile_path, 'r') as afile:
121
- actual_lines = afile.readlines()
122
- os.remove(afile_path)
123
- actfile.write(actual_lines[1])
124
- actual = [float(itm) for itm in actual_lines[1].split(',')]
125
- expect = [float(itm) for itm in expect_lines[idx].split(',')]
126
- if not numpy.allclose(actual, expect, atol=0.0, rtol=0.0):
127
- diffs = True
128
- idx += 1
129
- actfile.close()
116
+ with open(actfile_path, 'w', encoding='utf-8') as actfile:
117
+ actfile.write('rid,res1,res2,res3,res4\n')
118
+ idx = 1 # expect_lines list index
119
+ for rnum in range(1, num_reforms + 1):
120
+ afile_path = os.path.join(
121
+ tests_path, f'reform_actual_{rnum}.csv'
122
+ )
123
+ with open(afile_path, 'r', encoding='utf-8') as afile:
124
+ actual_lines = afile.readlines()
125
+ os.remove(afile_path)
126
+ actfile.write(actual_lines[1])
127
+ actual = [float(itm) for itm in actual_lines[1].split(',')]
128
+ expect = [float(itm) for itm in expect_lines[idx].split(',')]
129
+ if not numpy.allclose(actual, expect, atol=0.0, rtol=0.0):
130
+ diffs = True
131
+ idx += 1
130
132
  # remove init file
131
133
  os.remove(initfile)
132
134
  # remove 'reforms_actual.csv' file if no actual-vs-expect diffs
@@ -139,5 +141,4 @@ def fixture_test_reforms(tests_path):
139
141
  msg += '--- AND RERUN TEST. ---\n'
140
142
  msg += '-------------------------------------------------\n'
141
143
  raise ValueError(msg)
142
- else:
143
- os.remove(actfile_path)
144
+ os.remove(actfile_path)