taxcalc 4.6.1__py3-none-any.whl → 4.6.3__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 +2 -2
- taxcalc/cli/tc.py +85 -29
- taxcalc/parameters.py +188 -118
- taxcalc/policy.py +26 -4
- taxcalc/policy_current_law.json +2 -2
- taxcalc/reforms/REFORMS.md +0 -2
- taxcalc/reforms/ext.json +0 -2
- taxcalc/taxcalcio.py +95 -24
- taxcalc/tests/conftest.py +1 -1
- taxcalc/tests/reforms.json +8 -19
- taxcalc/tests/reforms_expect.csv +9 -10
- taxcalc/tests/test_4package.py +0 -1
- taxcalc/tests/test_parameters.py +18 -22
- taxcalc/tests/test_policy.py +153 -41
- taxcalc/tests/test_reforms.py +2 -2
- taxcalc/tests/test_taxcalcio.py +35 -13
- taxcalc/utils.py +32 -10
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/METADATA +5 -2
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/RECORD +24 -24
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/WHEEL +1 -1
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/entry_points.txt +0 -0
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/licenses/LICENSE +0 -0
- {taxcalc-4.6.1.dist-info → taxcalc-4.6.3.dist-info}/top_level.txt +0 -0
taxcalc/policy.py
CHANGED
@@ -8,6 +8,7 @@ Tax-Calculator federal tax policy Policy class.
|
|
8
8
|
import os
|
9
9
|
import json
|
10
10
|
from pathlib import Path
|
11
|
+
import paramtools
|
11
12
|
from taxcalc.parameters import Parameters
|
12
13
|
from taxcalc.growfactors import GrowFactors
|
13
14
|
|
@@ -131,13 +132,34 @@ class Policy(Parameters):
|
|
131
132
|
"""
|
132
133
|
return Parameters._read_json_revision(obj, 'policy')
|
133
134
|
|
134
|
-
def implement_reform(self, reform,
|
135
|
+
def implement_reform(self, reform: dict,
|
135
136
|
print_warnings=True, raise_errors=True):
|
136
137
|
"""
|
137
|
-
Implement reform using Tax-Calculator
|
138
|
-
may also use the adjust method with ParamTools styled reforms.
|
138
|
+
Implement reform using a Tax-Calculator-style reform dictionary.
|
139
139
|
"""
|
140
|
-
|
140
|
+
if not isinstance(reform, dict):
|
141
|
+
raise paramtools.ValidationError(
|
142
|
+
{'errors': {'schema': 'reform must be a dictionary'}},
|
143
|
+
None
|
144
|
+
)
|
145
|
+
deprecated_parameters = [
|
146
|
+
'ID_AmountCap_Switch',
|
147
|
+
'ID_AmountCap_rt',
|
148
|
+
'ID_BenefitCap_Switch',
|
149
|
+
'ID_BenefitCap_rt',
|
150
|
+
'ID_BenefitSurtax_Switch',
|
151
|
+
'ID_BenefitSurtax_crt',
|
152
|
+
'ID_BenefitSurtax_trt',
|
153
|
+
'ID_BenefitSurtax_em',
|
154
|
+
]
|
155
|
+
for param in reform.keys():
|
156
|
+
if param in deprecated_parameters:
|
157
|
+
print(
|
158
|
+
f'DEPRECATION WARNING: the {param} policy parameter\n'
|
159
|
+
'is scheduled to be removed in Tax-Calculator 5.0.0;\n'
|
160
|
+
'if you think this removal should not happen, open an\n'
|
161
|
+
'issue on GitHub to make your case for non-removal.'
|
162
|
+
)
|
141
163
|
return self._update(reform, print_warnings, raise_errors)
|
142
164
|
|
143
165
|
@staticmethod
|
taxcalc/policy_current_law.json
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
"validators": {
|
7
7
|
"range": {
|
8
8
|
"min": 2013,
|
9
|
-
"max":
|
9
|
+
"max": 2035
|
10
10
|
}
|
11
11
|
}
|
12
12
|
},
|
@@ -21819,7 +21819,7 @@
|
|
21819
21819
|
"validators": {
|
21820
21820
|
"range": {
|
21821
21821
|
"min": 0,
|
21822
|
-
"max":
|
21822
|
+
"max": 9e+99
|
21823
21823
|
}
|
21824
21824
|
},
|
21825
21825
|
"compatible_data": {
|
taxcalc/reforms/REFORMS.md
CHANGED
taxcalc/reforms/ext.json
CHANGED
@@ -34,8 +34,6 @@
|
|
34
34
|
"CTC_c": {"2026": 2000.00},
|
35
35
|
"ACTC_c": {"2026": 1776.67},
|
36
36
|
"ACTC_c-indexed": {"2026": true},
|
37
|
-
"ACTC_c": {"2032": 2000.00},
|
38
|
-
"ACTC_c-indexed": {"2032": false},
|
39
37
|
"ODC_c": {"2026": 500.00},
|
40
38
|
"CTC_ps": {"2026": [200000.0, 400000.0, 200000.0, 200000.0, 400000.0]},
|
41
39
|
"ACTC_Income_thd": {"2026": 2500.00},
|
taxcalc/taxcalcio.py
CHANGED
@@ -18,7 +18,7 @@ from taxcalc.consumption import Consumption
|
|
18
18
|
from taxcalc.growdiff import GrowDiff
|
19
19
|
from taxcalc.growfactors import GrowFactors
|
20
20
|
from taxcalc.calculator import Calculator
|
21
|
-
from taxcalc.utils import (delete_file, write_graph_file,
|
21
|
+
from taxcalc.utils import (json_to_dict, delete_file, write_graph_file,
|
22
22
|
add_quantile_table_row_variable,
|
23
23
|
unweighted_sum, weighted_sum)
|
24
24
|
|
@@ -54,6 +54,9 @@ class TaxCalcIO():
|
|
54
54
|
None implies economic assumptions are standard assumptions,
|
55
55
|
or string is name of optional ASSUMP file.
|
56
56
|
|
57
|
+
runid: int
|
58
|
+
run id value to use for simpler output file names
|
59
|
+
|
57
60
|
silent: boolean
|
58
61
|
whether or not to suppress action messages.
|
59
62
|
|
@@ -64,7 +67,7 @@ class TaxCalcIO():
|
|
64
67
|
# pylint: disable=too-many-instance-attributes
|
65
68
|
|
66
69
|
def __init__(self, input_data, tax_year, baseline, reform, assump,
|
67
|
-
silent=True):
|
70
|
+
runid=0, silent=True):
|
68
71
|
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
69
72
|
# pylint: disable=too-many-branches,too-many-statements,too-many-locals
|
70
73
|
self.silent = silent
|
@@ -133,7 +136,17 @@ class TaxCalcIO():
|
|
133
136
|
msg = f'{fname} does not end in .json'
|
134
137
|
self.errmsg += f'ERROR: BASELINE file name {msg}\n'
|
135
138
|
# check existence of BASELINE file
|
136
|
-
if
|
139
|
+
if os.path.isfile(bas):
|
140
|
+
# check validity of JSON text
|
141
|
+
with open(bas, 'r', encoding='utf-8') as jfile:
|
142
|
+
json_text = jfile.read()
|
143
|
+
try:
|
144
|
+
_ = json_to_dict(json_text)
|
145
|
+
except ValueError as valerr: # pragma: no cover
|
146
|
+
msg = f'{bas} contains invalid JSON'
|
147
|
+
self.errmsg += f'ERROR: BASELINE file {msg}\n'
|
148
|
+
self.errmsg += f'{valerr}'
|
149
|
+
else:
|
137
150
|
msg = f'{bas} could not be found'
|
138
151
|
self.errmsg += f'ERROR: BASELINE file {msg}\n'
|
139
152
|
# add fname to list of basnames used in output file names
|
@@ -167,7 +180,17 @@ class TaxCalcIO():
|
|
167
180
|
msg = f'{fname} does not end in .json'
|
168
181
|
self.errmsg += f'ERROR: REFORM file name {msg}\n'
|
169
182
|
# check existence of REFORM file
|
170
|
-
if
|
183
|
+
if os.path.isfile(rfm):
|
184
|
+
# check validity of JSON text
|
185
|
+
with open(rfm, 'r', encoding='utf-8') as jfile:
|
186
|
+
json_text = jfile.read()
|
187
|
+
try:
|
188
|
+
_ = json_to_dict(json_text)
|
189
|
+
except ValueError as valerr: # pragma: no cover
|
190
|
+
msg = f'{rfm} contains invalid JSON'
|
191
|
+
self.errmsg += f'ERROR: REFORM file {msg}\n'
|
192
|
+
self.errmsg += f'{valerr}'
|
193
|
+
else:
|
171
194
|
msg = f'{rfm} could not be found'
|
172
195
|
self.errmsg += f'ERROR: REFORM file {msg}\n'
|
173
196
|
# add fname to list of refnames used in output file names
|
@@ -205,6 +228,9 @@ class TaxCalcIO():
|
|
205
228
|
self.errmsg += f'ERROR: {msg}\n'
|
206
229
|
# create OUTPUT file name and delete any existing output files
|
207
230
|
self.output_filename = f'{inp}{bas}{ref}{asm}.xxx'
|
231
|
+
self.runid = runid
|
232
|
+
if runid > 0:
|
233
|
+
self.output_filename = f'run{runid}-{str(tax_year)[2:]}.xxx'
|
208
234
|
self.delete_output_files()
|
209
235
|
# initialize variables whose values are set in init method
|
210
236
|
self.calc_ref = None
|
@@ -215,13 +241,13 @@ class TaxCalcIO():
|
|
215
241
|
Delete all output files derived from self.output_filename.
|
216
242
|
"""
|
217
243
|
extensions = [
|
218
|
-
'-params.
|
219
|
-
'-params.
|
220
|
-
'
|
244
|
+
'-params.baseline',
|
245
|
+
'-params.reform',
|
246
|
+
'.tables',
|
221
247
|
'-atr.html',
|
222
248
|
'-mtr.html',
|
223
|
-
'-
|
224
|
-
'.
|
249
|
+
'-chg.html',
|
250
|
+
'.dumpdb',
|
225
251
|
]
|
226
252
|
for ext in extensions:
|
227
253
|
delete_file(self.output_filename.replace('.xxx', ext))
|
@@ -458,13 +484,18 @@ class TaxCalcIO():
|
|
458
484
|
"""
|
459
485
|
# update self.output_filename and delete output files
|
460
486
|
parts = self.output_filename.split('-')
|
461
|
-
|
487
|
+
if self.runid == 0: # if using legacy output file names
|
488
|
+
parts[1] = str(year)[2:]
|
489
|
+
else: # if using simpler output file names (runN-YY.xxx)
|
490
|
+
subparts = parts[1].split('.')
|
491
|
+
subparts[0] = str(year)[2:]
|
492
|
+
parts[1] = '.'.join(subparts)
|
462
493
|
self.output_filename = '-'.join(parts)
|
463
494
|
self.delete_output_files()
|
464
495
|
# advance baseline and reform Calculator objects to specified year
|
465
496
|
self.calc_bas.advance_to_year(year)
|
466
497
|
self.calc_ref.advance_to_year(year)
|
467
|
-
idata = 'Advance input data and
|
498
|
+
idata = 'Advance input data and' if aging_data else 'Advance'
|
468
499
|
if not self.silent:
|
469
500
|
print(f'{idata} policy to {year}')
|
470
501
|
|
@@ -550,21 +581,22 @@ class TaxCalcIO():
|
|
550
581
|
"""
|
551
582
|
Write baseline and reform policy parameter values to separate files.
|
552
583
|
"""
|
584
|
+
year = self.calc_bas.current_year
|
553
585
|
param_names = Policy.parameter_list()
|
554
|
-
fname = self.output_filename.replace('.xxx', '-params.
|
586
|
+
fname = self.output_filename.replace('.xxx', '-params.baseline')
|
555
587
|
with open(fname, 'w', encoding='utf-8') as pfile:
|
556
588
|
for pname in param_names:
|
557
589
|
pval = self.calc_bas.policy_param(pname)
|
558
|
-
pfile.write(f'{pname} {pval}\n')
|
590
|
+
pfile.write(f'{year} {pname} {pval}\n')
|
559
591
|
if not self.silent:
|
560
592
|
print( # pragma: no cover
|
561
593
|
f'Write baseline policy parameter values to file {fname}'
|
562
594
|
)
|
563
|
-
fname = self.output_filename.replace('.xxx', '-params.
|
595
|
+
fname = self.output_filename.replace('.xxx', '-params.reform')
|
564
596
|
with open(fname, 'w', encoding='utf-8') as pfile:
|
565
597
|
for pname in param_names:
|
566
598
|
pval = self.calc_ref.policy_param(pname)
|
567
|
-
pfile.write(f'{pname} {pval}\n')
|
599
|
+
pfile.write(f'{year} {pname} {pval}\n')
|
568
600
|
if not self.silent:
|
569
601
|
print( # pragma: no cover
|
570
602
|
f'Write reform policy parameter values to file {fname}'
|
@@ -575,7 +607,7 @@ class TaxCalcIO():
|
|
575
607
|
Write tables to text file.
|
576
608
|
"""
|
577
609
|
# pylint: disable=too-many-locals
|
578
|
-
tab_fname = self.output_filename.replace('.xxx', '
|
610
|
+
tab_fname = self.output_filename.replace('.xxx', '.tables')
|
579
611
|
# skip tables if there are not some positive weights
|
580
612
|
if self.calc_bas.total_weight() <= 0.:
|
581
613
|
with open(tab_fname, 'w', encoding='utf-8') as tfile:
|
@@ -600,10 +632,21 @@ class TaxCalcIO():
|
|
600
632
|
diff = nontax + change # using expanded_income under baseline policy
|
601
633
|
diffdf = pd.DataFrame(data=np.column_stack(diff), columns=all_vars)
|
602
634
|
# write each kind of distributional table
|
635
|
+
year = self.calc_bas.current_year
|
603
636
|
with open(tab_fname, 'w', encoding='utf-8') as tfile:
|
604
|
-
TaxCalcIO.write_decile_table(
|
637
|
+
TaxCalcIO.write_decile_table(
|
638
|
+
distdf,
|
639
|
+
tfile,
|
640
|
+
year,
|
641
|
+
tkind='Reform Totals',
|
642
|
+
)
|
605
643
|
tfile.write('\n')
|
606
|
-
TaxCalcIO.write_decile_table(
|
644
|
+
TaxCalcIO.write_decile_table(
|
645
|
+
diffdf,
|
646
|
+
tfile,
|
647
|
+
year,
|
648
|
+
tkind='Differences',
|
649
|
+
)
|
607
650
|
# delete intermediate DataFrame objects
|
608
651
|
del distdf
|
609
652
|
del diffdf
|
@@ -614,7 +657,7 @@ class TaxCalcIO():
|
|
614
657
|
)
|
615
658
|
|
616
659
|
@staticmethod
|
617
|
-
def write_decile_table(dfx, tfile, tkind='Totals'):
|
660
|
+
def write_decile_table(dfx, tfile, year, tkind='Totals'):
|
618
661
|
"""
|
619
662
|
Write to tfile the tkind decile table using dfx DataFrame.
|
620
663
|
"""
|
@@ -642,7 +685,10 @@ class TaxCalcIO():
|
|
642
685
|
weighted_sum, 'combined', include_groups=False
|
643
686
|
).values[:, 1]
|
644
687
|
# write decile table to text file
|
645
|
-
row =
|
688
|
+
row = (
|
689
|
+
f'Weighted Tax {tkind} by '
|
690
|
+
f'Baseline Expanded-Income Decile for {year}\n'
|
691
|
+
)
|
646
692
|
tfile.write(row)
|
647
693
|
# pylint: disable=consider-using-f-string
|
648
694
|
rowfmt = '{}{}{}{}{}{}\n'
|
@@ -696,8 +742,8 @@ class TaxCalcIO():
|
|
696
742
|
pos_wght_sum = self.calc_ref.total_weight() > 0.0
|
697
743
|
fig = None
|
698
744
|
# percentage-aftertax-income-change graph
|
699
|
-
pch_fname = self.output_filename.replace('.xxx', '-
|
700
|
-
pch_title = '
|
745
|
+
pch_fname = self.output_filename.replace('.xxx', '-chg.html')
|
746
|
+
pch_title = 'CHG by Income Percentile'
|
701
747
|
if pos_wght_sum:
|
702
748
|
fig = self.calc_bas.pch_graph(self.calc_ref, pop_quantiles=False)
|
703
749
|
write_graph_file(fig, pch_fname, pch_title)
|
@@ -824,16 +870,41 @@ class TaxCalcIO():
|
|
824
870
|
# begin main logic
|
825
871
|
assert isinstance(dump_varlist, list)
|
826
872
|
assert len(dump_varlist) > 0
|
827
|
-
db_fname = self.output_filename.replace('.xxx', '.
|
873
|
+
db_fname = self.output_filename.replace('.xxx', '.dumpdb')
|
828
874
|
dbcon = sqlite3.connect(db_fname)
|
829
875
|
# write base table
|
830
876
|
outdf = pd.DataFrame()
|
831
877
|
for var in TaxCalcIO.BASE_DUMPVARS:
|
832
878
|
outdf[var] = self.calc_bas.array(var)
|
833
|
-
|
879
|
+
expanded_income_bin_edges = [ # default income_group definition
|
880
|
+
-1e300, # essentially -infinity
|
881
|
+
50e3,
|
882
|
+
100e3,
|
883
|
+
250e3,
|
884
|
+
500e3,
|
885
|
+
1e6,
|
886
|
+
1e300, # essentially +infinity
|
887
|
+
]
|
888
|
+
outdf['income_group'] = 1 + pd.cut( # default base.income_group values
|
889
|
+
outdf['expanded_income'],
|
890
|
+
expanded_income_bin_edges,
|
891
|
+
right=False, # bins are defined as [lo_edge, hi_edge)
|
892
|
+
labels=False, # pd.cut returns bins numbered 0,1,2,...
|
893
|
+
)
|
834
894
|
assert len(outdf.index) == self.calc_bas.array_len
|
835
895
|
outdf.to_sql('base', dbcon, index=False)
|
836
896
|
del outdf
|
897
|
+
# write income_group_definition table
|
898
|
+
num_groups = len(expanded_income_bin_edges) - 1
|
899
|
+
outdf = pd.DataFrame()
|
900
|
+
outdf['income_group'] = np.array([
|
901
|
+
1 + grp for grp in range(0, num_groups)
|
902
|
+
])
|
903
|
+
outdf['income_lower'] = np.array(expanded_income_bin_edges[:-1])
|
904
|
+
outdf['income_up_to'] = np.array(expanded_income_bin_edges[1:])
|
905
|
+
assert len(outdf.index) == num_groups
|
906
|
+
outdf.to_sql('income_group_definition', dbcon, index=False)
|
907
|
+
del outdf
|
837
908
|
# write baseline table
|
838
909
|
outdf = dump_output(
|
839
910
|
self.calc_bas, dump_varlist, mtr_itax_bas, mtr_ptax_bas,
|
taxcalc/tests/conftest.py
CHANGED
@@ -71,7 +71,7 @@ def fixture_test_reforms(tests_path):
|
|
71
71
|
Execute logic only once rather than on each pytest-xdist node.
|
72
72
|
"""
|
73
73
|
# pylint: disable=too-many-locals,too-many-statements
|
74
|
-
num_reforms =
|
74
|
+
num_reforms = 63 # must be same as NUM_REFORMS in test_reforms.py
|
75
75
|
handling_logic = ('PYTEST_XDIST_WORKER' not in os.environ or
|
76
76
|
os.environ['PYTEST_XDIST_WORKER'] == 'gw0')
|
77
77
|
initfile = os.path.join(tests_path, 'reforms_actual_init')
|
taxcalc/tests/reforms.json
CHANGED
@@ -543,17 +543,6 @@
|
|
543
543
|
},
|
544
544
|
|
545
545
|
"55": {
|
546
|
-
"baseline": "2017_law.json",
|
547
|
-
"start_year": 2017,
|
548
|
-
"value": {"ID_AmountCap_rt": 0.02,
|
549
|
-
"ID_AmountCap_Switch":
|
550
|
-
[false, true, true, false, false, false, false]},
|
551
|
-
"name": "Limit amount of S&L deduction to 2% AGI",
|
552
|
-
"output_type": "iitax",
|
553
|
-
"compare_with": {"Budget Options": [44.1, 86.6, 87.1, 91.2]}
|
554
|
-
},
|
555
|
-
|
556
|
-
"56": {
|
557
546
|
"baseline": "policy_current_law.json",
|
558
547
|
"start_year": 2017,
|
559
548
|
"value": {"parameter_indexing_CPI_offset": 0},
|
@@ -562,7 +551,7 @@
|
|
562
551
|
"compare_with": {}
|
563
552
|
},
|
564
553
|
|
565
|
-
"
|
554
|
+
"56": {
|
566
555
|
"baseline": "2017_law.json",
|
567
556
|
"start_year": 2017,
|
568
557
|
"value": {"PT_rt7": 0.35,
|
@@ -572,7 +561,7 @@
|
|
572
561
|
"compare_with": {}
|
573
562
|
},
|
574
563
|
|
575
|
-
"
|
564
|
+
"57": {
|
576
565
|
"baseline": "2017_law.json",
|
577
566
|
"start_year": 2017,
|
578
567
|
"value": {"PT_wages_active_income": true,
|
@@ -583,7 +572,7 @@
|
|
583
572
|
"compare_with": {}
|
584
573
|
},
|
585
574
|
|
586
|
-
"
|
575
|
+
"58": {
|
587
576
|
"baseline": "2017_law.json",
|
588
577
|
"start_year": 2017,
|
589
578
|
"value": {"CTC_new_c": 1000,
|
@@ -595,7 +584,7 @@
|
|
595
584
|
"compare_with": {}
|
596
585
|
},
|
597
586
|
|
598
|
-
"
|
587
|
+
"59": {
|
599
588
|
"baseline": "2017_law.json",
|
600
589
|
"start_year": 2017,
|
601
590
|
"value": {"CTC_new_c": 1000,
|
@@ -608,7 +597,7 @@
|
|
608
597
|
"compare_with": {}
|
609
598
|
},
|
610
599
|
|
611
|
-
"
|
600
|
+
"60": {
|
612
601
|
"baseline": "2017_law.json",
|
613
602
|
"start_year": 2017,
|
614
603
|
"value": {"ID_Charity_hc": 1,
|
@@ -619,7 +608,7 @@
|
|
619
608
|
"compare_with": {}
|
620
609
|
},
|
621
610
|
|
622
|
-
"
|
611
|
+
"61": {
|
623
612
|
"baseline": "2017_law.json",
|
624
613
|
"start_year": 2019,
|
625
614
|
"value": {"EITC_basic_frac": 0.5},
|
@@ -628,7 +617,7 @@
|
|
628
617
|
"compare_with": {}
|
629
618
|
},
|
630
619
|
|
631
|
-
"
|
620
|
+
"62": {
|
632
621
|
"baseline": "policy_current_law.json",
|
633
622
|
"start_year": 2019,
|
634
623
|
"value": {"EITC_indiv": true},
|
@@ -637,7 +626,7 @@
|
|
637
626
|
"compare_with": {}
|
638
627
|
},
|
639
628
|
|
640
|
-
"
|
629
|
+
"63": {
|
641
630
|
"baseline": "policy_current_law.json",
|
642
631
|
"start_year": 2019,
|
643
632
|
"value": {"RPTC_rt": 0.062,
|
taxcalc/tests/reforms_expect.csv
CHANGED
@@ -53,13 +53,12 @@ rid,res1,res2,res3,res4
|
|
53
53
|
52,-187.0,-187.5,-193.6,-201.6
|
54
54
|
53,-130.0,-129.3,-132.7,-137.7
|
55
55
|
54,30.7,33.2,31.4,41.0
|
56
|
-
55,
|
57
|
-
56
|
58
|
-
57,-
|
59
|
-
58,-
|
60
|
-
59,-
|
61
|
-
60,-
|
62
|
-
61,-
|
63
|
-
62,-
|
64
|
-
63,-
|
65
|
-
64,-46.9,-48.1,-49.2,-52.0
|
56
|
+
55,0.0,0.0,0.0,0.0
|
57
|
+
56,-14.1,-15.6,-15.9,-14.4
|
58
|
+
57,-15.5,-17.0,-17.4,-15.8
|
59
|
+
58,-66.9,-66.9,-67.0,-67.1
|
60
|
+
59,-67.3,-67.2,-67.3,-67.4
|
61
|
+
60,-1.0,-0.6,-0.8,-1.7
|
62
|
+
61,-17.4,-18.0,-24.3,-20.0
|
63
|
+
62,-12.7,-13.0,-23.4,-13.8
|
64
|
+
63,-46.9,-48.1,-49.2,-52.0
|
taxcalc/tests/test_4package.py
CHANGED
taxcalc/tests/test_parameters.py
CHANGED
@@ -11,8 +11,8 @@ import json
|
|
11
11
|
import math
|
12
12
|
import tempfile
|
13
13
|
import numpy as np
|
14
|
-
import paramtools
|
15
14
|
import pytest
|
15
|
+
import paramtools
|
16
16
|
from taxcalc.parameters import Parameters, is_paramtools_format
|
17
17
|
from taxcalc.policy import Policy
|
18
18
|
from taxcalc.consumption import Consumption
|
@@ -125,7 +125,8 @@ def test_params_class(revision, expect, params_json_file):
|
|
125
125
|
"""
|
126
126
|
The Params class is derived from the abstract base Parameter class.
|
127
127
|
"""
|
128
|
-
# pylint: disable=
|
128
|
+
# pylint: disable=abstract-method
|
129
|
+
|
129
130
|
DEFAULTS_FILE_NAME = params_json_file.name
|
130
131
|
DEFAULTS_FILE_PATH = ''
|
131
132
|
START_YEAR = 2001
|
@@ -143,6 +144,8 @@ def test_params_class(revision, expect, params_json_file):
|
|
143
144
|
"""
|
144
145
|
self._update(revision, print_warnings, raise_errors)
|
145
146
|
|
147
|
+
# intentionally do not define a set_rates(self) method
|
148
|
+
|
146
149
|
# test Params class
|
147
150
|
prms = Params()
|
148
151
|
|
@@ -190,9 +193,6 @@ def test_json_file_contents(tests_path, fname):
|
|
190
193
|
first_year = Policy.JSON_START_YEAR
|
191
194
|
last_known_year = Policy.LAST_KNOWN_YEAR # for indexed parameter values
|
192
195
|
known_years = set(range(first_year, last_known_year + 1))
|
193
|
-
# for TCJA-reverting long_params
|
194
|
-
long_known_years = set(range(first_year, last_known_year + 1))
|
195
|
-
long_known_years.add(2026)
|
196
196
|
# check elements in each parameter sub-dictionary
|
197
197
|
failures = ''
|
198
198
|
path = os.path.join(tests_path, "..", fname)
|
@@ -284,7 +284,6 @@ def test_parameters_mentioned(tests_path, jfname, pfname):
|
|
284
284
|
path = os.path.join(tests_path, '..', pfname)
|
285
285
|
with open(path, 'r', encoding='utf-8') as pfile:
|
286
286
|
code_text = pfile.read()
|
287
|
-
# pylint: enable=consider-using-join
|
288
287
|
# check that each param (without leading _) is mentioned in code text
|
289
288
|
for pname in allparams:
|
290
289
|
if pname == "schema":
|
@@ -294,6 +293,7 @@ def test_parameters_mentioned(tests_path, jfname, pfname):
|
|
294
293
|
|
295
294
|
# following tests access private methods, so pylint: disable=protected-access
|
296
295
|
|
296
|
+
|
297
297
|
class ArrayParams(Parameters):
|
298
298
|
"""ArrayParams class"""
|
299
299
|
defaults = {
|
@@ -498,10 +498,9 @@ def test_expand_2d_already_filled():
|
|
498
498
|
"""
|
499
499
|
One of several _expand_?D tests.
|
500
500
|
"""
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
[40000., 74900., 37450., 50200., 74900., 37450.]]
|
501
|
+
ii_brk2 = [[36000., 72250., 36500., 48600., 72500., 36250.],
|
502
|
+
[38000., 74000., 36900., 49400., 73800., 36900.],
|
503
|
+
[40000., 74900., 37450., 50200., 74900., 37450.]]
|
505
504
|
|
506
505
|
years = [2013, 2014, 2015]
|
507
506
|
params = ArrayParams(
|
@@ -509,27 +508,28 @@ def test_expand_2d_already_filled():
|
|
509
508
|
label_to_extend=None,
|
510
509
|
)
|
511
510
|
params.adjust({
|
512
|
-
"II_brk2": params.from_array("II_brk2", np.array(
|
511
|
+
"II_brk2": params.from_array("II_brk2", np.array(ii_brk2), year=years)
|
513
512
|
})
|
514
513
|
|
515
514
|
params.extend(
|
516
515
|
params=["II_brk2"], label="year", label_values=years
|
517
516
|
)
|
518
517
|
res = params.to_array("II_brk2", year=years)
|
519
|
-
assert np.allclose(res, np.array(
|
518
|
+
assert np.allclose(res, np.array(ii_brk2), atol=0.01, rtol=0.0)
|
520
519
|
|
521
520
|
|
522
521
|
def test_expand_2d_partial_expand():
|
523
522
|
"""
|
524
523
|
One of several _expand_?D tests.
|
525
524
|
"""
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
525
|
+
ii_brk2 = [
|
526
|
+
[36000.0, 72250.0, 36500.0, 48600.0, 72500.0, 36250.0],
|
527
|
+
[38000.0, 74000.0, 36900.0, 49400.0, 73800.0, 36900.0],
|
528
|
+
[40000.0, 74900.0, 37450.0, 50200.0, 74900.0, 37450.0],
|
529
|
+
]
|
530
530
|
# We have three years worth of data, need 4 years worth,
|
531
531
|
# but we only need the inflation rate for year 3 to go
|
532
|
-
# from year 3
|
532
|
+
# from year 3 to year 4
|
533
533
|
inf_rates = [0.02, 0.02, 0.03]
|
534
534
|
exp1 = 40000. * 1.03
|
535
535
|
exp2 = 74900. * 1.03
|
@@ -545,11 +545,7 @@ def test_expand_2d_partial_expand():
|
|
545
545
|
years = [2013, 2014, 2015]
|
546
546
|
params = ArrayParams(array_first=False, label_to_extend=None)
|
547
547
|
params.adjust({
|
548
|
-
"II_brk2": params.from_array(
|
549
|
-
"II_brk2",
|
550
|
-
np.array(_II_brk2),
|
551
|
-
year=years
|
552
|
-
)
|
548
|
+
"II_brk2": params.from_array("II_brk2", np.array(ii_brk2), year=years)
|
553
549
|
})
|
554
550
|
params._inflation_rates[:3] = inf_rates
|
555
551
|
params.extend(
|