taxcalc 4.4.0__py3-none-any.whl → 4.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. taxcalc/__init__.py +1 -1
  2. taxcalc/calcfunctions.py +326 -171
  3. taxcalc/calculator.py +35 -34
  4. taxcalc/cli/tc.py +6 -7
  5. taxcalc/consumption.py +9 -4
  6. taxcalc/data.py +8 -8
  7. taxcalc/decorators.py +3 -3
  8. taxcalc/growdiff.py +5 -0
  9. taxcalc/growfactors.py +1 -1
  10. taxcalc/parameters.py +85 -42
  11. taxcalc/policy.py +1 -1
  12. taxcalc/records.py +1 -0
  13. taxcalc/records_variables.json +6 -0
  14. taxcalc/taxcalcio.py +49 -44
  15. taxcalc/tests/cmpi_cps_expect.txt +6 -6
  16. taxcalc/tests/cmpi_puf_expect.txt +6 -6
  17. taxcalc/tests/conftest.py +42 -41
  18. taxcalc/tests/test_4package.py +9 -7
  19. taxcalc/tests/test_benefits.py +9 -8
  20. taxcalc/tests/test_calcfunctions.py +55 -38
  21. taxcalc/tests/test_calculator.py +11 -6
  22. taxcalc/tests/test_compare.py +44 -50
  23. taxcalc/tests/test_compatible_data.py +9 -7
  24. taxcalc/tests/test_consumption.py +38 -18
  25. taxcalc/tests/test_cpscsv.py +33 -31
  26. taxcalc/tests/test_data.py +31 -24
  27. taxcalc/tests/test_decorators.py +84 -32
  28. taxcalc/tests/test_growdiff.py +16 -13
  29. taxcalc/tests/test_growfactors.py +8 -8
  30. taxcalc/tests/test_parameters.py +54 -58
  31. taxcalc/tests/test_policy.py +14 -12
  32. taxcalc/tests/test_puf_var_stats.py +14 -14
  33. taxcalc/tests/test_pufcsv.py +40 -40
  34. taxcalc/tests/test_records.py +73 -60
  35. taxcalc/tests/test_reforms.py +34 -31
  36. taxcalc/tests/test_responses.py +4 -4
  37. taxcalc/tests/test_taxcalcio.py +76 -62
  38. taxcalc/tests/test_utils.py +78 -46
  39. taxcalc/utils.py +49 -42
  40. taxcalc/validation/taxsim35/taxsim_emulation.json +1 -5
  41. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/METADATA +19 -5
  42. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/RECORD +46 -46
  43. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/WHEEL +1 -1
  44. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/LICENSE +0 -0
  45. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/entry_points.txt +0 -0
  46. {taxcalc-4.4.0.dist-info → taxcalc-4.4.1.dist-info}/top_level.txt +0 -0
@@ -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 # pylint: disable=import-error
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='module', name='reformfile0')
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
- rfile = tempfile.NamedTemporaryFile(suffix='.json', mode='a', delete=False)
41
- rfile.write(txt + '\n')
42
- rfile.close()
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='module', name='assumpfile0')
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
- afile.write(contents)
65
- afile.close()
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='module', name='reformfile1')
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
- rfile.write(contents)
108
- rfile.close()
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='module', name='baselinebad')
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
- rfile.write(contents)
125
- rfile.close()
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='module', name='errorreformfile')
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
- rfile.write(contents)
142
- rfile.close()
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='module', name='errorassumpfile')
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
- rfile.write(contents)
165
- rfile.close()
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='module', name='assumpfile1')
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
- afile.write(contents)
188
- afile.close()
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='module', name='lumpsumreformfile')
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
- rfile.write(lumpsum_reform_contents)
205
- rfile.close()
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='module', name='assumpfile2')
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
- afile.write(assump2_contents)
228
- afile.close()
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
- (list(), list(), list(), list(), list()),
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-locals,too-many-branches
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)_ok' == 'no'
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)_ok' == 'no'
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)_ok' == 'no'
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 = '{}+{}'.format(reformfile1.name, reformfile1.name)
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)_ok' == 'no'
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 = dict()
527
- idict['RECID'] = [i for i in range(1, nobs + 1)]
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 = dict()
575
- idict['RECID'] = [i for i in range(1, nobs + 1)]
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 = dict()
612
- idict['RECID'] = [i for i in range(1, nobs + 1)]
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='module', name='warnreformfile')
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
- rfile.write(contents)
656
- rfile.close()
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='module', name='reformfile9')
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
- rfile.write(contents)
706
- rfile.close()
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='module', name='regression_reform_file')
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
- rfile.write(contents)
726
- rfile.close()
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,