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.
- taxcalc/__init__.py +1 -1
- taxcalc/calcfunctions.py +326 -171
- taxcalc/calculator.py +35 -34
- taxcalc/cli/tc.py +6 -7
- taxcalc/consumption.json +1 -1
- taxcalc/consumption.py +9 -4
- taxcalc/cps_weights.csv.gz +0 -0
- taxcalc/data.py +8 -8
- taxcalc/decorators.py +3 -3
- taxcalc/growdiff.json +1 -1
- taxcalc/growdiff.py +5 -0
- taxcalc/growfactors.csv +26 -25
- taxcalc/growfactors.py +1 -1
- taxcalc/parameters.py +85 -42
- taxcalc/policy.py +2 -2
- taxcalc/policy_current_law.json +87 -87
- taxcalc/puf_ratios.csv +15 -14
- taxcalc/puf_weights.csv.gz +0 -0
- taxcalc/records.py +1 -0
- taxcalc/records_variables.json +6 -0
- taxcalc/reforms/ext.json +21 -21
- taxcalc/taxcalcio.py +49 -44
- taxcalc/tests/cmpi_cps_expect.txt +6 -6
- taxcalc/tests/cmpi_puf_expect.txt +6 -6
- taxcalc/tests/conftest.py +43 -42
- taxcalc/tests/cpscsv_agg_expect.csv +22 -22
- taxcalc/tests/puf_var_wght_means_by_year.csv +70 -70
- taxcalc/tests/pufcsv_agg_expect.csv +22 -22
- taxcalc/tests/test_4package.py +9 -7
- taxcalc/tests/test_benefits.py +9 -8
- taxcalc/tests/test_calcfunctions.py +55 -38
- taxcalc/tests/test_calculator.py +11 -6
- taxcalc/tests/test_compare.py +45 -51
- taxcalc/tests/test_compatible_data.py +9 -7
- taxcalc/tests/test_consumption.py +38 -18
- taxcalc/tests/test_cpscsv.py +33 -31
- taxcalc/tests/test_data.py +31 -24
- taxcalc/tests/test_decorators.py +84 -32
- taxcalc/tests/test_growdiff.py +16 -13
- taxcalc/tests/test_growfactors.py +8 -8
- taxcalc/tests/test_parameters.py +55 -59
- taxcalc/tests/test_policy.py +14 -12
- 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 +35 -32
- 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.4.0.dist-info → taxcalc-4.5.0.dist-info}/METADATA +19 -5
- {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/RECORD +57 -57
- {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/WHEEL +1 -1
- {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/LICENSE +0 -0
- {taxcalc-4.4.0.dist-info → taxcalc-4.5.0.dist-info}/entry_points.txt +0 -0
- {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.
|
3
|
-
// WITH 2025-to-2026 INDEXING FACTOR = 1.
|
4
|
-
// AND 2028-to-2029 INDEXING FACTOR = 1.
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
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": [
|
40
|
-
"AMT_em_ps": {"2026": [
|
41
|
-
"AMT_em_pe": {"2026":
|
42
|
-
"STD": {"2026": [
|
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": [
|
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": [
|
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-
|
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,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-
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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-
|
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
|
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}'
|
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 = (
|
780
|
-
|
781
|
-
|
782
|
-
|
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
|
-
|
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')
|
@@ -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 =
|
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
|
-
|
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)
|