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.
- taxcalc/__init__.py +1 -1
- taxcalc/calcfunctions.py +326 -171
- taxcalc/calculator.py +35 -34
- taxcalc/cli/tc.py +6 -7
- taxcalc/consumption.py +14 -9
- taxcalc/data.py +8 -8
- taxcalc/decorators.py +3 -3
- taxcalc/growdiff.py +6 -6
- taxcalc/growfactors.py +1 -1
- taxcalc/parameters.py +91 -47
- taxcalc/policy.py +23 -7
- taxcalc/records.py +1 -0
- taxcalc/records_variables.json +6 -0
- taxcalc/reforms/ext.json +1 -1
- taxcalc/taxcalcio.py +88 -73
- taxcalc/tests/cmpi_cps_expect.txt +6 -6
- taxcalc/tests/cmpi_puf_expect.txt +6 -6
- taxcalc/tests/conftest.py +42 -41
- taxcalc/tests/test_4package.py +47 -3
- taxcalc/tests/test_benefits.py +9 -8
- taxcalc/tests/test_calcfunctions.py +55 -38
- taxcalc/tests/test_calculator.py +14 -10
- taxcalc/tests/test_compare.py +44 -50
- taxcalc/tests/test_compatible_data.py +9 -7
- taxcalc/tests/test_consumption.py +41 -22
- taxcalc/tests/test_cpscsv.py +81 -31
- taxcalc/tests/test_data.py +31 -24
- taxcalc/tests/test_decorators.py +84 -32
- taxcalc/tests/test_growdiff.py +20 -19
- taxcalc/tests/test_growfactors.py +8 -8
- taxcalc/tests/test_parameters.py +54 -58
- taxcalc/tests/test_policy.py +16 -14
- taxcalc/tests/test_puf_var_stats.py +14 -14
- taxcalc/tests/test_pufcsv.py +40 -40
- taxcalc/tests/test_records.py +73 -60
- taxcalc/tests/test_reforms.py +34 -31
- taxcalc/tests/test_responses.py +4 -4
- taxcalc/tests/test_taxcalcio.py +76 -62
- taxcalc/tests/test_utils.py +78 -46
- taxcalc/utils.py +49 -42
- taxcalc/validation/taxsim35/taxsim_emulation.json +1 -5
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/METADATA +23 -11
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/RECORD +47 -47
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/WHEEL +1 -1
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/LICENSE +0 -0
- {taxcalc-4.3.5.dist-info → taxcalc-4.4.1.dist-info}/entry_points.txt +0 -0
- {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-
|
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 = '{
|
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'
|
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'
|
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'
|
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'
|
112
|
+
self.errmsg += f'ERROR: {msg}\n'
|
113
113
|
elif isinstance(input_data, pd.DataFrame):
|
114
|
-
inp = 'df-{
|
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'
|
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 = '-{
|
127
|
+
bas = f'-{fname[:-5]}'
|
128
128
|
else:
|
129
129
|
msg = 'BASELINE file name does not end in .json'
|
130
|
-
self.errmsg += 'ERROR: {}\n'
|
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'
|
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'
|
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'
|
154
|
-
self.errmsg += 'ERROR: REFORM file name {}\n'
|
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'
|
158
|
-
self.errmsg += 'ERROR: REFORM file {}\n'
|
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 += '{
|
168
|
+
ref += f'{refname[:-5]}'
|
169
169
|
else:
|
170
170
|
msg = 'TaxCalcIO.ctor: reform is neither None nor str'
|
171
|
-
self.errmsg += 'ERROR: {}\n'
|
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 = '-{
|
181
|
+
asm = f'-{fname[:-5]}'
|
182
182
|
else:
|
183
183
|
msg = 'ASSUMP file name does not end in .json'
|
184
|
-
self.errmsg += 'ERROR: {}\n'
|
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'
|
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'
|
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'
|
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'
|
206
|
+
self.errmsg += f'ERROR: {msg}\n'
|
207
207
|
# create OUTPUT file name and delete any existing output files
|
208
|
-
output_filename = '{}{}{}{}.csv'
|
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-
|
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
|
273
|
-
#
|
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
|
285
|
-
#
|
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
|
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
|
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
|
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
|
-
|
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-
|
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
|
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}'
|
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 = (
|
770
|
-
|
771
|
-
|
772
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
143
|
-
os.remove(actfile_path)
|
144
|
+
os.remove(actfile_path)
|