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/tests/test_taxcalcio.py
CHANGED
@@ -12,7 +12,7 @@ from io import StringIO
|
|
12
12
|
import tempfile
|
13
13
|
import pytest
|
14
14
|
import pandas as pd
|
15
|
-
from taxcalc import TaxCalcIO
|
15
|
+
from taxcalc import TaxCalcIO
|
16
16
|
|
17
17
|
|
18
18
|
RAWINPUT = (
|
@@ -24,7 +24,7 @@ RAWINPUT = (
|
|
24
24
|
)
|
25
25
|
|
26
26
|
|
27
|
-
@pytest.fixture(scope='
|
27
|
+
@pytest.fixture(scope='session', name='reformfile0')
|
28
28
|
def fixture_reformfile0():
|
29
29
|
"""
|
30
30
|
Specify JSON reform file.
|
@@ -37,9 +37,10 @@ def fixture_reformfile0():
|
|
37
37
|
}
|
38
38
|
}
|
39
39
|
"""
|
40
|
-
|
41
|
-
|
42
|
-
rfile
|
40
|
+
with tempfile.NamedTemporaryFile(
|
41
|
+
suffix='.json', mode='a', delete=False
|
42
|
+
) as rfile:
|
43
|
+
rfile.write(txt + '\n')
|
43
44
|
yield rfile
|
44
45
|
if os.path.isfile(rfile.name):
|
45
46
|
try:
|
@@ -48,12 +49,11 @@ def fixture_reformfile0():
|
|
48
49
|
pass # sometimes we can't remove a generated temporary file
|
49
50
|
|
50
51
|
|
51
|
-
@pytest.fixture(scope='
|
52
|
+
@pytest.fixture(scope='session', name='assumpfile0')
|
52
53
|
def fixture_assumpfile0():
|
53
54
|
"""
|
54
55
|
Temporary assumption file with .json extension.
|
55
56
|
"""
|
56
|
-
afile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
57
57
|
contents = """
|
58
58
|
{
|
59
59
|
"consumption": {},
|
@@ -61,8 +61,10 @@ def fixture_assumpfile0():
|
|
61
61
|
"growdiff_response": {}
|
62
62
|
}
|
63
63
|
"""
|
64
|
-
|
65
|
-
|
64
|
+
with tempfile.NamedTemporaryFile(
|
65
|
+
suffix='.json', mode='a', delete=False
|
66
|
+
) as afile:
|
67
|
+
afile.write(contents)
|
66
68
|
yield afile
|
67
69
|
if os.path.isfile(afile.name):
|
68
70
|
try:
|
@@ -71,12 +73,11 @@ def fixture_assumpfile0():
|
|
71
73
|
pass # sometimes we can't remove a generated temporary file
|
72
74
|
|
73
75
|
|
74
|
-
@pytest.fixture(scope='
|
76
|
+
@pytest.fixture(scope='session', name='reformfile1')
|
75
77
|
def fixture_reformfile1():
|
76
78
|
"""
|
77
79
|
Temporary reform file with .json extension.
|
78
80
|
"""
|
79
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
80
81
|
contents = """
|
81
82
|
{"policy": {
|
82
83
|
"AMT_brk1": { // top of first AMT tax bracket
|
@@ -104,8 +105,10 @@ def fixture_reformfile1():
|
|
104
105
|
}
|
105
106
|
}
|
106
107
|
"""
|
107
|
-
|
108
|
-
|
108
|
+
with tempfile.NamedTemporaryFile(
|
109
|
+
suffix='.json', mode='a', delete=False
|
110
|
+
) as rfile:
|
111
|
+
rfile.write(contents)
|
109
112
|
yield rfile
|
110
113
|
if os.path.isfile(rfile.name):
|
111
114
|
try:
|
@@ -114,15 +117,16 @@ def fixture_reformfile1():
|
|
114
117
|
pass # sometimes we can't remove a generated temporary file
|
115
118
|
|
116
119
|
|
117
|
-
@pytest.fixture(scope='
|
120
|
+
@pytest.fixture(scope='session', name='baselinebad')
|
118
121
|
def fixture_baselinebad():
|
119
122
|
"""
|
120
123
|
Temporary baseline file with .json extension.
|
121
124
|
"""
|
122
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
123
125
|
contents = '{ "policy": {"AMT_brk1": {"2011": 0.0}}}'
|
124
|
-
|
125
|
-
|
126
|
+
with tempfile.NamedTemporaryFile(
|
127
|
+
suffix='.json', mode='a', delete=False
|
128
|
+
) as rfile:
|
129
|
+
rfile.write(contents)
|
126
130
|
yield rfile
|
127
131
|
if os.path.isfile(rfile.name):
|
128
132
|
try:
|
@@ -131,15 +135,16 @@ def fixture_baselinebad():
|
|
131
135
|
pass # sometimes we can't remove a generated temporary file
|
132
136
|
|
133
137
|
|
134
|
-
@pytest.fixture(scope='
|
138
|
+
@pytest.fixture(scope='session', name='errorreformfile')
|
135
139
|
def fixture_errorreformfile():
|
136
140
|
"""
|
137
141
|
Temporary reform file with .json extension.
|
138
142
|
"""
|
139
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
140
143
|
contents = '{ "policy": {"xxx": {"2015": 0}}}'
|
141
|
-
|
142
|
-
|
144
|
+
with tempfile.NamedTemporaryFile(
|
145
|
+
suffix='.json', mode='a', delete=False
|
146
|
+
) as rfile:
|
147
|
+
rfile.write(contents)
|
143
148
|
yield rfile
|
144
149
|
if os.path.isfile(rfile.name):
|
145
150
|
try:
|
@@ -148,12 +153,11 @@ def fixture_errorreformfile():
|
|
148
153
|
pass # sometimes we can't remove a generated temporary file
|
149
154
|
|
150
155
|
|
151
|
-
@pytest.fixture(scope='
|
156
|
+
@pytest.fixture(scope='session', name='errorassumpfile')
|
152
157
|
def fixture_errorassumpfile():
|
153
158
|
"""
|
154
159
|
Temporary assumption file with .json extension.
|
155
160
|
"""
|
156
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
157
161
|
contents = """
|
158
162
|
{
|
159
163
|
"consumption": {"MPC_e18400": {"2018": -9}},
|
@@ -161,8 +165,10 @@ def fixture_errorassumpfile():
|
|
161
165
|
"growdiff_response": {"ABOOKxx": {"2017": 0.02}}
|
162
166
|
}
|
163
167
|
"""
|
164
|
-
|
165
|
-
|
168
|
+
with tempfile.NamedTemporaryFile(
|
169
|
+
suffix='.json', mode='a', delete=False
|
170
|
+
) as rfile:
|
171
|
+
rfile.write(contents)
|
166
172
|
yield rfile
|
167
173
|
if os.path.isfile(rfile.name):
|
168
174
|
try:
|
@@ -171,12 +177,11 @@ def fixture_errorassumpfile():
|
|
171
177
|
pass # sometimes we can't remove a generated temporary file
|
172
178
|
|
173
179
|
|
174
|
-
@pytest.fixture(scope='
|
180
|
+
@pytest.fixture(scope='session', name='assumpfile1')
|
175
181
|
def fixture_assumpfile1():
|
176
182
|
"""
|
177
183
|
Temporary assumption file with .json extension.
|
178
184
|
"""
|
179
|
-
afile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
180
185
|
contents = """
|
181
186
|
{
|
182
187
|
"consumption": { "MPC_e18400": {"2018": 0.05} },
|
@@ -184,8 +189,10 @@ def fixture_assumpfile1():
|
|
184
189
|
"growdiff_response": {}
|
185
190
|
}
|
186
191
|
"""
|
187
|
-
|
188
|
-
|
192
|
+
with tempfile.NamedTemporaryFile(
|
193
|
+
suffix='.json', mode='a', delete=False
|
194
|
+
) as afile:
|
195
|
+
afile.write(contents)
|
189
196
|
yield afile
|
190
197
|
if os.path.isfile(afile.name):
|
191
198
|
try:
|
@@ -194,15 +201,16 @@ def fixture_assumpfile1():
|
|
194
201
|
pass # sometimes we can't remove a generated temporary file
|
195
202
|
|
196
203
|
|
197
|
-
@pytest.fixture(scope='
|
204
|
+
@pytest.fixture(scope='session', name='lumpsumreformfile')
|
198
205
|
def fixture_lumpsumreformfile():
|
199
206
|
"""
|
200
207
|
Temporary reform file without .json extension.
|
201
208
|
"""
|
202
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
203
209
|
lumpsum_reform_contents = '{"policy": {"LST": {"2013": 200}}}'
|
204
|
-
|
205
|
-
|
210
|
+
with tempfile.NamedTemporaryFile(
|
211
|
+
suffix='.json', mode='a', delete=False
|
212
|
+
) as rfile:
|
213
|
+
rfile.write(lumpsum_reform_contents)
|
206
214
|
yield rfile
|
207
215
|
if os.path.isfile(rfile.name):
|
208
216
|
try:
|
@@ -211,12 +219,11 @@ def fixture_lumpsumreformfile():
|
|
211
219
|
pass # sometimes we can't remove a generated temporary file
|
212
220
|
|
213
221
|
|
214
|
-
@pytest.fixture(scope='
|
222
|
+
@pytest.fixture(scope='session', name='assumpfile2')
|
215
223
|
def fixture_assumpfile2():
|
216
224
|
"""
|
217
225
|
Temporary assumption file with .json extension.
|
218
226
|
"""
|
219
|
-
afile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
220
227
|
assump2_contents = """
|
221
228
|
{
|
222
229
|
"consumption": {"BEN_snap_value": {"2018": 0.90}},
|
@@ -224,8 +231,10 @@ def fixture_assumpfile2():
|
|
224
231
|
"growdiff_response": {}
|
225
232
|
}
|
226
233
|
"""
|
227
|
-
|
228
|
-
|
234
|
+
with tempfile.NamedTemporaryFile(
|
235
|
+
suffix='.json', mode='a', delete=False
|
236
|
+
) as afile:
|
237
|
+
afile.write(assump2_contents)
|
229
238
|
yield afile
|
230
239
|
if os.path.isfile(afile.name):
|
231
240
|
try:
|
@@ -238,7 +247,7 @@ def fixture_assumpfile2():
|
|
238
247
|
('no-dot-csv-filename', 'no-dot-json-filename',
|
239
248
|
'no-dot-json-filename',
|
240
249
|
'no-dot-json-filename', 'no-output-directory'),
|
241
|
-
(
|
250
|
+
([], [], [], [], []),
|
242
251
|
('no-exist.csv', 'no-exist.json', 'no-exist.json', 'no-exist.json', '.'),
|
243
252
|
])
|
244
253
|
def test_ctor_errors(input_data, baseline, reform, assump, outdir):
|
@@ -262,7 +271,8 @@ def test_init_errors(reformfile0, errorreformfile, errorassumpfile,
|
|
262
271
|
"""
|
263
272
|
Ensure error messages generated correctly by TaxCalcIO.init method.
|
264
273
|
"""
|
265
|
-
# pylint: disable=too-many-arguments,too-many-
|
274
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
275
|
+
# pylint: disable=too-many-locals,too-many-branches
|
266
276
|
recdict = {'RECID': 1, 'MARS': 1, 'e00300': 100000, 's006': 1e8}
|
267
277
|
recdf = pd.DataFrame(data=recdict, index=[0])
|
268
278
|
# test TaxCalcIO ctor
|
@@ -422,7 +432,7 @@ def test_output_options(reformfile1, assumpfile1):
|
|
422
432
|
os.remove(outfilepath)
|
423
433
|
except OSError:
|
424
434
|
pass # sometimes we can't remove a generated temporary file
|
425
|
-
assert 'TaxCalcIO.analyze(minimal_output)
|
435
|
+
assert False, 'TaxCalcIO.analyze(minimal_output) failed'
|
426
436
|
# --dump output with full dump
|
427
437
|
try:
|
428
438
|
tcio.analyze(writing_output_file=True, output_dump=True)
|
@@ -432,7 +442,7 @@ def test_output_options(reformfile1, assumpfile1):
|
|
432
442
|
os.remove(outfilepath)
|
433
443
|
except OSError:
|
434
444
|
pass # sometimes we can't remove a generated temporary file
|
435
|
-
assert 'TaxCalcIO.analyze(full_dump_output)
|
445
|
+
assert False, 'TaxCalcIO.analyze(full_dump_output) failed'
|
436
446
|
# --dump output with partial dump
|
437
447
|
try:
|
438
448
|
tcio.analyze(writing_output_file=True,
|
@@ -444,7 +454,7 @@ def test_output_options(reformfile1, assumpfile1):
|
|
444
454
|
os.remove(outfilepath)
|
445
455
|
except OSError:
|
446
456
|
pass # sometimes we can't remove a generated temporary file
|
447
|
-
assert 'TaxCalcIO.analyze(partial_dump_output)
|
457
|
+
assert False, 'TaxCalcIO.analyze(partial_dump_output) failed'
|
448
458
|
# if tries were successful, remove doc file and output file
|
449
459
|
docfilepath = outfilepath.replace('.csv', '-doc.text')
|
450
460
|
if os.path.isfile(docfilepath):
|
@@ -458,7 +468,7 @@ def test_write_doc_file(reformfile1, assumpfile1):
|
|
458
468
|
Test write_doc_file with compound reform.
|
459
469
|
"""
|
460
470
|
taxyear = 2021
|
461
|
-
compound_reform = '{}+{
|
471
|
+
compound_reform = f'{reformfile1.name}+{reformfile1.name}'
|
462
472
|
tcio = TaxCalcIO(input_data=pd.read_csv(StringIO(RAWINPUT)),
|
463
473
|
tax_year=taxyear,
|
464
474
|
baseline=None,
|
@@ -510,7 +520,7 @@ def test_sqldb_option(reformfile1, assumpfile1):
|
|
510
520
|
os.remove(dbfilepath)
|
511
521
|
except OSError:
|
512
522
|
pass # sometimes we can't remove a generated temporary file
|
513
|
-
assert 'TaxCalcIO.analyze(sqldb)
|
523
|
+
assert False, 'ERROR: TaxCalcIO.analyze(sqldb) failed'
|
514
524
|
# if try was successful, remove the db file
|
515
525
|
if os.path.isfile(dbfilepath):
|
516
526
|
os.remove(dbfilepath)
|
@@ -523,8 +533,8 @@ def test_no_tables_or_graphs(reformfile1):
|
|
523
533
|
"""
|
524
534
|
# create input sample that cannot output tables or graphs
|
525
535
|
nobs = 10
|
526
|
-
idict =
|
527
|
-
idict['RECID'] =
|
536
|
+
idict = {}
|
537
|
+
idict['RECID'] = list(range(1, nobs + 1))
|
528
538
|
idict['MARS'] = [2 for i in range(1, nobs + 1)]
|
529
539
|
idict['s006'] = [0.0 for i in range(1, nobs + 1)]
|
530
540
|
idict['e00300'] = [10000 * i for i in range(1, nobs + 1)]
|
@@ -571,8 +581,8 @@ def test_tables(reformfile1):
|
|
571
581
|
"""
|
572
582
|
# create tabable input
|
573
583
|
nobs = 100
|
574
|
-
idict =
|
575
|
-
idict['RECID'] =
|
584
|
+
idict = {}
|
585
|
+
idict['RECID'] = list(range(1, nobs + 1))
|
576
586
|
idict['MARS'] = [2 for i in range(1, nobs + 1)]
|
577
587
|
idict['s006'] = [10.0 for i in range(1, nobs + 1)]
|
578
588
|
idict['e00300'] = [10000 * i for i in range(1, nobs + 1)]
|
@@ -608,8 +618,8 @@ def test_graphs(reformfile1):
|
|
608
618
|
"""
|
609
619
|
# create graphable input
|
610
620
|
nobs = 100
|
611
|
-
idict =
|
612
|
-
idict['RECID'] =
|
621
|
+
idict = {}
|
622
|
+
idict['RECID'] = list(range(1, nobs + 1))
|
613
623
|
idict['MARS'] = [2 for i in range(1, nobs + 1)]
|
614
624
|
idict['XTOT'] = [3 for i in range(1, nobs + 1)]
|
615
625
|
idict['s006'] = [10.0 for i in range(1, nobs + 1)]
|
@@ -645,15 +655,16 @@ def test_graphs(reformfile1):
|
|
645
655
|
os.remove(fname)
|
646
656
|
|
647
657
|
|
648
|
-
@pytest.fixture(scope='
|
658
|
+
@pytest.fixture(scope='session', name='warnreformfile')
|
649
659
|
def fixture_warnreformfile():
|
650
660
|
"""
|
651
661
|
Temporary reform file with .json extension.
|
652
662
|
"""
|
653
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
654
663
|
contents = '{"policy": {"STD_Dep": {"2015": 0}}}'
|
655
|
-
|
656
|
-
|
664
|
+
with tempfile.NamedTemporaryFile(
|
665
|
+
suffix='.json', mode='a', delete=False
|
666
|
+
) as rfile:
|
667
|
+
rfile.write(contents)
|
657
668
|
yield rfile
|
658
669
|
if os.path.isfile(rfile.name):
|
659
670
|
try:
|
@@ -687,12 +698,11 @@ def test_analyze_warnings_print(warnreformfile):
|
|
687
698
|
assert tcio.tax_year() == taxyear
|
688
699
|
|
689
700
|
|
690
|
-
@pytest.fixture(scope='
|
701
|
+
@pytest.fixture(scope='session', name='reformfile9')
|
691
702
|
def fixture_reformfile9():
|
692
703
|
"""
|
693
704
|
Temporary reform file with .json extension.
|
694
705
|
"""
|
695
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
696
706
|
contents = """
|
697
707
|
{ "policy": {
|
698
708
|
"SS_Earnings_c": {
|
@@ -702,8 +712,10 @@ def fixture_reformfile9():
|
|
702
712
|
}
|
703
713
|
}
|
704
714
|
"""
|
705
|
-
|
706
|
-
|
715
|
+
with tempfile.NamedTemporaryFile(
|
716
|
+
suffix='.json', mode='a', delete=False
|
717
|
+
) as rfile:
|
718
|
+
rfile.write(contents)
|
707
719
|
yield rfile
|
708
720
|
if os.path.isfile(rfile.name):
|
709
721
|
try:
|
@@ -712,7 +724,7 @@ def fixture_reformfile9():
|
|
712
724
|
pass # sometimes we can't remove a generated temporary file
|
713
725
|
|
714
726
|
|
715
|
-
@pytest.fixture(scope='
|
727
|
+
@pytest.fixture(scope='session', name='regression_reform_file')
|
716
728
|
def fixture_regression_reform_file():
|
717
729
|
"""
|
718
730
|
Temporary reform file with .json extension.
|
@@ -720,10 +732,11 @@ def fixture_regression_reform_file():
|
|
720
732
|
Example causing regression reported in issue:
|
721
733
|
https://github.com/PSLmodels/Tax-Calculator/issues/2622
|
722
734
|
"""
|
723
|
-
rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
|
724
735
|
contents = '{ "policy": {"AMEDT_rt": {"2021": 1.8}}}'
|
725
|
-
|
726
|
-
|
736
|
+
with tempfile.NamedTemporaryFile(
|
737
|
+
suffix='.json', mode='a', delete=False
|
738
|
+
) as rfile:
|
739
|
+
rfile.write(contents)
|
727
740
|
yield rfile
|
728
741
|
if os.path.isfile(rfile.name):
|
729
742
|
try:
|
@@ -733,6 +746,7 @@ def fixture_regression_reform_file():
|
|
733
746
|
|
734
747
|
|
735
748
|
def test_error_message_parsed_correctly(regression_reform_file):
|
749
|
+
"""Test docstring"""
|
736
750
|
tcio = TaxCalcIO(input_data=pd.read_csv(StringIO(RAWINPUT)),
|
737
751
|
tax_year=2022,
|
738
752
|
baseline=regression_reform_file.name,
|