taxcalc 4.3.5__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 (47) 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 +14 -9
  6. taxcalc/data.py +8 -8
  7. taxcalc/decorators.py +3 -3
  8. taxcalc/growdiff.py +6 -6
  9. taxcalc/growfactors.py +1 -1
  10. taxcalc/parameters.py +91 -47
  11. taxcalc/policy.py +23 -7
  12. taxcalc/records.py +1 -0
  13. taxcalc/records_variables.json +6 -0
  14. taxcalc/reforms/ext.json +1 -1
  15. taxcalc/taxcalcio.py +88 -73
  16. taxcalc/tests/cmpi_cps_expect.txt +6 -6
  17. taxcalc/tests/cmpi_puf_expect.txt +6 -6
  18. taxcalc/tests/conftest.py +42 -41
  19. taxcalc/tests/test_4package.py +47 -3
  20. taxcalc/tests/test_benefits.py +9 -8
  21. taxcalc/tests/test_calcfunctions.py +55 -38
  22. taxcalc/tests/test_calculator.py +14 -10
  23. taxcalc/tests/test_compare.py +44 -50
  24. taxcalc/tests/test_compatible_data.py +9 -7
  25. taxcalc/tests/test_consumption.py +41 -22
  26. taxcalc/tests/test_cpscsv.py +81 -31
  27. taxcalc/tests/test_data.py +31 -24
  28. taxcalc/tests/test_decorators.py +84 -32
  29. taxcalc/tests/test_growdiff.py +20 -19
  30. taxcalc/tests/test_growfactors.py +8 -8
  31. taxcalc/tests/test_parameters.py +54 -58
  32. taxcalc/tests/test_policy.py +16 -14
  33. taxcalc/tests/test_puf_var_stats.py +14 -14
  34. taxcalc/tests/test_pufcsv.py +40 -40
  35. taxcalc/tests/test_records.py +73 -60
  36. taxcalc/tests/test_reforms.py +34 -31
  37. taxcalc/tests/test_responses.py +4 -4
  38. taxcalc/tests/test_taxcalcio.py +76 -62
  39. taxcalc/tests/test_utils.py +78 -46
  40. taxcalc/utils.py +49 -42
  41. taxcalc/validation/taxsim35/taxsim_emulation.json +1 -5
  42. {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/METADATA +23 -11
  43. {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/RECORD +47 -47
  44. {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/WHEEL +1 -1
  45. {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/LICENSE +0 -0
  46. {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/entry_points.txt +0 -0
  47. {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/top_level.txt +0 -0
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,9 +246,38 @@ 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
+ # instantiate base and reform GrowFactors objects
253
+ if self.tmd_input_data:
254
+ gfactors_base = GrowFactors(self.tmd_gfactor) # pragma: no cover
255
+ gfactors_ref = GrowFactors(self.tmd_gfactor) # pragma: no cover
256
+ else:
257
+ gfactors_base = GrowFactors()
258
+ gfactors_ref = GrowFactors()
259
+ # check tax_year validity
260
+ max_tax_year = gfactors_base.last_year
261
+ if tax_year > max_tax_year:
262
+ msg = f'TAXYEAR={tax_year} is greater than {max_tax_year}'
263
+ self.errmsg += f'ERROR: {msg}\n'
264
+ if self.puf_input_data:
265
+ min_tax_year = max( # pragma: no cover
266
+ Policy.JSON_START_YEAR, Records.PUFCSV_YEAR)
267
+ elif self.cps_input_data:
268
+ min_tax_year = max(
269
+ Policy.JSON_START_YEAR, Records.CPSCSV_YEAR)
270
+ elif self.tmd_input_data:
271
+ min_tax_year = max( # pragma: no cover
272
+ Policy.JSON_START_YEAR, Records.TMDCSV_YEAR)
273
+ else:
274
+ min_tax_year = Policy.JSON_START_YEAR
275
+ if tax_year < min_tax_year:
276
+ msg = f'TAXYEAR={tax_year} is less than {min_tax_year}'
277
+ self.errmsg += f'ERROR: {msg}\n'
278
+ # tax_year out of valid range means cannot proceed with calculations
279
+ if self.errmsg:
280
+ return
252
281
  # get policy parameter dictionary from --baseline file
253
282
  basedict = Calculator.read_json_param_objects(baseline, None)
254
283
  # get assumption sub-dictionaries
@@ -264,35 +293,29 @@ class TaxCalcIO():
264
293
  # remember parameters for reform documentation
265
294
  self.param_dict = paramdict
266
295
  self.policy_dicts = policydicts
296
+ # set last_b_year
297
+ last_b_year = max(tax_year, Policy.LAST_BUDGET_YEAR)
267
298
  # create gdiff_baseline object
268
- gdiff_baseline = GrowDiff()
299
+ gdiff_baseline = GrowDiff(last_budget_year=last_b_year)
269
300
  try:
270
301
  gdiff_baseline.update_growdiff(paramdict['growdiff_baseline'])
271
302
  except paramtools.ValidationError as valerr_msg:
272
- self.errmsg += valerr_msg.__str__()
273
- # create GrowFactors base object that incorporates gdiff_baseline
274
- if self.tmd_input_data:
275
- gfactors_base = GrowFactors(self.tmd_gfactor) # pragma: no cover
276
- else:
277
- gfactors_base = GrowFactors()
303
+ self.errmsg += str(valerr_msg)
304
+ # apply gdiff_baseline to gfactor_base
278
305
  gdiff_baseline.apply_to(gfactors_base)
279
306
  # specify gdiff_response object
280
- gdiff_response = GrowDiff()
307
+ gdiff_response = GrowDiff(last_budget_year=last_b_year)
281
308
  try:
282
309
  gdiff_response.update_growdiff(paramdict['growdiff_response'])
283
310
  except paramtools.ValidationError as valerr_msg:
284
- self.errmsg += valerr_msg.__str__()
285
- # create GrowFactors ref object that has all gdiff objects applied
286
- if self.tmd_input_data:
287
- gfactors_ref = GrowFactors(self.tmd_gfactor) # pragma: no cover
288
- else:
289
- gfactors_ref = GrowFactors()
311
+ self.errmsg += str(valerr_msg)
312
+ # apply gdiff_baseline and gdiff_response to gfactor_ref
290
313
  gdiff_baseline.apply_to(gfactors_ref)
291
314
  gdiff_response.apply_to(gfactors_ref)
292
315
  self.gf_reform = copy.deepcopy(gfactors_ref)
293
316
  # create Policy objects:
294
317
  # ... the baseline Policy object
295
- base = Policy(gfactors=gfactors_base)
318
+ base = Policy(gfactors=gfactors_base, last_budget_year=last_b_year)
296
319
  try:
297
320
  base.implement_reform(basedict['policy'],
298
321
  print_warnings=True,
@@ -300,10 +323,10 @@ class TaxCalcIO():
300
323
  for _, errors in base.parameter_errors.items():
301
324
  self.errmsg += "\n".join(errors)
302
325
  except paramtools.ValidationError as valerr_msg:
303
- self.errmsg += valerr_msg.__str__()
326
+ self.errmsg += str(valerr_msg)
304
327
  # ... the reform Policy object
305
328
  if self.specified_reform:
306
- pol = Policy(gfactors=gfactors_ref)
329
+ pol = Policy(gfactors=gfactors_ref, last_budget_year=last_b_year)
307
330
  for poldict in policydicts:
308
331
  try:
309
332
  pol.implement_reform(poldict,
@@ -314,24 +337,15 @@ class TaxCalcIO():
314
337
  for _, errors in pol.parameter_errors.items():
315
338
  self.errmsg += "\n".join(errors)
316
339
  except paramtools.ValidationError as valerr_msg:
317
- self.errmsg += valerr_msg.__str__()
340
+ self.errmsg += str(valerr_msg)
318
341
  else:
319
- pol = Policy(gfactors=gfactors_base)
342
+ pol = Policy(gfactors=gfactors_base, last_budget_year=last_b_year)
320
343
  # create Consumption object
321
- con = Consumption()
344
+ con = Consumption(last_budget_year=last_b_year)
322
345
  try:
323
346
  con.update_consumption(paramdict['consumption'])
324
347
  except paramtools.ValidationError as valerr_msg:
325
- self.errmsg += valerr_msg.__str__()
326
- # check for valid tax_year value
327
- if tax_year < pol.start_year:
328
- msg = 'tax_year {} less than policy.start_year {}'
329
- msg = msg.format(tax_year, pol.start_year)
330
- self.errmsg += 'ERROR: {}\n'.format(msg)
331
- if tax_year > pol.end_year:
332
- msg = 'tax_year {} greater than policy.end_year {}'
333
- msg = msg.format(tax_year, pol.end_year)
334
- self.errmsg += 'ERROR: {}\n'.format(msg)
348
+ self.errmsg += str(valerr_msg)
335
349
  # any errors imply cannot proceed with calculations
336
350
  if self.errmsg:
337
351
  return
@@ -388,10 +402,6 @@ class TaxCalcIO():
388
402
  adjust_ratios=None,
389
403
  exact_calculations=exact_calculations)
390
404
  recs_base = copy.deepcopy(recs)
391
- if tax_year < recs.data_year:
392
- msg = 'tax_year {} less than records.data_year {}'
393
- msg = msg.format(tax_year, recs.data_year)
394
- self.errmsg += 'ERROR: {}\n'.format(msg)
395
405
  # create Calculator objects
396
406
  self.calc = Calculator(policy=pol, records=recs,
397
407
  verbose=True,
@@ -421,9 +431,8 @@ class TaxCalcIO():
421
431
  valid_set = recs_vinfo.USABLE_READ_VARS | recs_vinfo.CALCULATED_VARS
422
432
  for var in dump_vars_list:
423
433
  if var not in valid_set:
424
- msg = 'invalid variable name in tcdumpvars file: {}'
425
- msg = msg.format(var)
426
- self.errmsg += 'ERROR: {}\n'.format(msg)
434
+ msg = f'invalid variable name in tcdumpvars file: {var}'
435
+ self.errmsg += f'ERROR: {msg}\n'
427
436
  # add essential variables even if not on custom list
428
437
  if 'RECID' not in dump_vars_list:
429
438
  dump_vars_list.append('RECID')
@@ -484,7 +493,8 @@ class TaxCalcIO():
484
493
  -------
485
494
  Nothing
486
495
  """
487
- # 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
488
498
  if self.puf_input_data and self.calc.reform_warnings:
489
499
  warn = 'PARAMETER VALUE WARNING(S): {}\n{}{}' # pragma: no cover
490
500
  print( # pragma: no cover
@@ -589,6 +599,7 @@ class TaxCalcIO():
589
599
  """
590
600
  Write dump output to SQLite3 database table dump.
591
601
  """
602
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
592
603
  db_fname = self._output_filename.replace('.csv', '.db')
593
604
  dbcon = sqlite3.connect(db_fname)
594
605
  # write baseline table
@@ -675,8 +686,9 @@ class TaxCalcIO():
675
686
  weighted_sum, 'combined', include_groups=False
676
687
  ).values[:, 1]
677
688
  # write decile table to text file
678
- row = 'Weighted Tax {} by Baseline Expanded-Income Decile\n'
679
- 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
680
692
  rowfmt = '{}{}{}{}{}{}\n'
681
693
  row = rowfmt.format(' Returns',
682
694
  ' ExpInc',
@@ -694,7 +706,7 @@ class TaxCalcIO():
694
706
  tfile.write(row)
695
707
  rowfmt = '{:9.2f}{:10.1f}{:10.1f}{:10.1f}{:10.1f}{:10.1f}\n'
696
708
  for decile in range(0, 10):
697
- row = '{:2d}'.format(decile)
709
+ row = f'{decile:2d}'
698
710
  row += rowfmt.format(rtns_series[decile] * 1e-6,
699
711
  xinc_series[decile] * 1e-9,
700
712
  itax_series[decile] * 1e-9,
@@ -710,6 +722,7 @@ class TaxCalcIO():
710
722
  htax_series.sum() * 1e-9,
711
723
  ctax_series.sum() * 1e-9)
712
724
  tfile.write(row)
725
+ # pylint: enable=consider-using-f-string
713
726
  del gdfx
714
727
  del rtns_series
715
728
  del xinc_series
@@ -766,10 +779,12 @@ class TaxCalcIO():
766
779
  """
767
780
  Write HTML graph file with title but no graph for specified reason.
768
781
  """
769
- txt = ('<html>\n'
770
- '<head><title>{}</title></head>\n'
771
- '<body><center<h1>{}</h1></center></body>\n'
772
- '</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
+ )
773
788
  with open(fname, 'w', encoding='utf-8') as gfile:
774
789
  gfile.write(txt)
775
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')
@@ -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)