taxcalc 4.6.0__py3-none-any.whl → 4.6.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 CHANGED
@@ -14,6 +14,6 @@ from taxcalc.taxcalcio import *
14
14
  from taxcalc.utils import *
15
15
  from taxcalc.cli import *
16
16
 
17
- __version__ = '4.6.0'
17
+ __version__ = '4.6.1'
18
18
  __min_python3_version__ = 10
19
19
  __max_python3_version__ = 12
taxcalc/calculator.py CHANGED
@@ -305,11 +305,11 @@ class Calculator():
305
305
  return self.__consumption.benval_params()
306
306
 
307
307
  @property
308
- def reform_warnings(self):
308
+ def reform_errors(self):
309
309
  """
310
- Calculator class embedded Policy object's parameter_warnings.
310
+ Calculator class embedded Policy object's parameter_errors.
311
311
  """
312
- return self.__policy.parameter_warnings
312
+ return self.__policy.parameter_errors
313
313
 
314
314
  @property
315
315
  def current_year(self):
taxcalc/cli/tc.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Command-line interface (CLI) to Tax-Calculator,
3
- which can be accessed as 'tc' from an installed taxcalc conda package.
3
+ which can be accessed as 'tc' from an installed taxcalc package.
4
4
  """
5
5
  # CODING-STYLE CHECKS:
6
6
  # pycodestyle tc.py
@@ -30,7 +30,7 @@ def cli_tc_main():
30
30
 
31
31
  # parse command-line arguments:
32
32
  usage_str = 'tc INPUT TAXYEAR {}{}{}{}'.format(
33
- '[--help]\n',
33
+ '[--help] [--numyears N]\n',
34
34
  (
35
35
  ' '
36
36
  '[--baseline BASELINE] [--reform REFORM] '
@@ -63,15 +63,26 @@ def cli_tc_main():
63
63
  'are computed.'),
64
64
  type=int,
65
65
  default=0)
66
+ parser.add_argument('--numyears', metavar='N',
67
+ help=('N is an integer indicating for how many '
68
+ 'years taxes are calculated. No --numyears '
69
+ 'implies calculations are done only for '
70
+ 'TAXYEAR. N greater than one implies output '
71
+ 'is written to separate files for each year '
72
+ 'beginning with TAXYEAR.'),
73
+ type=int,
74
+ default=1)
66
75
  parser.add_argument('--baseline',
67
76
  help=('BASELINE is name of optional JSON reform file. '
77
+ 'A compound reform can be specified using 2+ '
78
+ 'file names separated by plus (+) character(s). '
68
79
  'No --baseline implies baseline policy is '
69
80
  'current-law policy.'),
70
81
  default=None)
71
82
  parser.add_argument('--reform',
72
83
  help=('REFORM is name of optional JSON reform file. '
73
- 'A compound reform can be specified using two '
74
- 'file names separated by a plus (+) character. '
84
+ 'A compound reform can be specified using 2+ '
85
+ 'file names separated by plus (+) character(s). '
75
86
  'No --reform implies reform policy is '
76
87
  'current-law policy).'),
77
88
  default=None)
@@ -167,11 +178,54 @@ def cli_tc_main():
167
178
  _write_test_files()
168
179
  inputfn = TEST_INPUT_FILENAME
169
180
  taxyear = TEST_TAXYEAR
181
+ args.numyears = 1
170
182
  args.dumpdb = True
171
183
  else:
172
184
  inputfn = args.INPUT
173
185
  taxyear = args.TAXYEAR
174
- # instantiate TaxCalcIO object and do tax analysis
186
+ # check taxyear value
187
+ if taxyear > tc.Policy.LAST_BUDGET_YEAR:
188
+ msg = f'ERROR: TAXYEAR is greater than {tc.Policy.LAST_BUDGET_YEAR}\n'
189
+ sys.stderr.write(msg)
190
+ sys.stderr.write('USAGE: tc --help\n')
191
+ return 1
192
+ # check numyears value
193
+ if args.numyears < 1:
194
+ msg = 'ERROR: --numyears parameter N is less than one\n'
195
+ sys.stderr.write(msg)
196
+ sys.stderr.write('USAGE: tc --help\n')
197
+ return 1
198
+ max_numyears = tc.Policy.LAST_BUDGET_YEAR - taxyear + 1
199
+ if args.numyears > max_numyears:
200
+ msg = f'ERROR: --numyears parameter N is greater than {max_numyears}\n'
201
+ sys.stderr.write(msg)
202
+ sys.stderr.write('USAGE: tc --help\n')
203
+ return 1
204
+ # specify if aging input data
205
+ aging_data = (
206
+ inputfn.endswith('puf.csv') or
207
+ inputfn.endswith('cps.csv') or
208
+ inputfn.endswith('tmd.csv')
209
+ )
210
+ # check args.dumpdb and args.dumpvars consistency
211
+ if not args.dumpdb and args.dumpvars:
212
+ msg = 'ERROR: DUMPVARS file specified without --dumpdb option\n'
213
+ sys.stderr.write(msg)
214
+ sys.stderr.write('USAGE: tc --help\n')
215
+ return 1
216
+ # specify dumpvars_str from args.dumpvars file
217
+ dumpvars_str = ''
218
+ if args.dumpvars:
219
+ if os.path.exists(args.dumpvars):
220
+ with open(args.dumpvars, 'r', encoding='utf-8') as dfile:
221
+ dumpvars_str = dfile.read()
222
+ else:
223
+ msg = f'ERROR: DUMPVARS file {args.dumpvars} does not exist\n'
224
+ sys.stderr.write(msg)
225
+ sys.stderr.write('USAGE: tc --help\n')
226
+ return 1
227
+ # do calculations for taxyear
228
+ # ... initialize TaxCalcIO object for taxyear
175
229
  tcio = tc.TaxCalcIO(
176
230
  input_data=inputfn,
177
231
  tax_year=taxyear,
@@ -187,17 +241,13 @@ def cli_tc_main():
187
241
  sys.stderr.write(tcio.errmsg + '\n')
188
242
  sys.stderr.write('USAGE: tc --help\n')
189
243
  return 1
190
- aging = (
191
- inputfn.endswith('puf.csv') or
192
- inputfn.endswith('cps.csv') or
193
- inputfn.endswith('tmd.csv')
194
- )
195
244
  tcio.init(
196
245
  input_data=inputfn,
197
246
  tax_year=taxyear,
198
247
  baseline=args.baseline,
199
- reform=args.reform, assump=args.assump,
200
- aging_input_data=aging,
248
+ reform=args.reform,
249
+ assump=args.assump,
250
+ aging_input_data=aging_data,
201
251
  exact_calculations=args.exact,
202
252
  )
203
253
  if tcio.errmsg:
@@ -207,23 +257,7 @@ def cli_tc_main():
207
257
  sys.stderr.write(tcio.errmsg + '\n')
208
258
  sys.stderr.write('USAGE: tc --help\n')
209
259
  return 1
210
- # check args.dumpdb and args.dumpvars consistency
211
- if not args.dumpdb and args.dumpvars:
212
- msg = 'ERROR: DUMPVARS file specified without --dumpdb option\n'
213
- sys.stderr.write(msg)
214
- sys.stderr.write('USAGE: tc --help\n')
215
- return 1
216
- # construct dumpvars set from dumpvars_str
217
- dumpvars_str = ''
218
- if args.dumpvars:
219
- if os.path.exists(args.dumpvars):
220
- with open(args.dumpvars, 'r', encoding='utf-8') as dfile:
221
- dumpvars_str = dfile.read()
222
- else:
223
- msg = f'ERROR: DUMPVARS file {args.dumpvars} does not exist\n'
224
- sys.stderr.write(msg)
225
- sys.stderr.write('USAGE: tc --help\n')
226
- return 1
260
+ # ... conduct tax analysis for taxyear
227
261
  dumpvars_list = tcio.dump_variables(dumpvars_str)
228
262
  if tcio.errmsg:
229
263
  if tcio.errmsg.endswith('\n'):
@@ -232,7 +266,6 @@ def cli_tc_main():
232
266
  sys.stderr.write(tcio.errmsg + '\n')
233
267
  sys.stderr.write('USAGE: tc --help\n')
234
268
  return 1
235
- # conduct tax analysis
236
269
  tcio.analyze(
237
270
  output_params=args.params,
238
271
  output_tables=args.tables,
@@ -244,6 +277,23 @@ def cli_tc_main():
244
277
  if args.test:
245
278
  retcode = _compare_test_output_files()
246
279
  return retcode
280
+ # quit if args.numyears is equal to one
281
+ if args.numyears == 1:
282
+ if not args.silent:
283
+ print( # pragma: no cover
284
+ f'Execution time is {(time.time() - start_time):.1f} seconds'
285
+ )
286
+ return 0
287
+ # analyze years after taxyear if args.numyears is greater than one
288
+ for xyear in range(1, args.numyears):
289
+ tcio.advance_to_year(taxyear + xyear, aging_data)
290
+ tcio.analyze(
291
+ output_params=args.params,
292
+ output_tables=args.tables,
293
+ output_graphs=args.graphs,
294
+ output_dump=args.dumpdb,
295
+ dump_varlist=dumpvars_list,
296
+ )
247
297
  if not args.silent:
248
298
  print( # pragma: no cover
249
299
  f'Execution time is {(time.time() - start_time):.1f} seconds'
taxcalc/data.py CHANGED
@@ -147,8 +147,8 @@ class Data():
147
147
 
148
148
  def increment_year(self):
149
149
  """
150
- Add one to current year; and also does
151
- extrapolation & reweighting for new current year if aged_data is True.
150
+ Add one to current year; and also does extrapolation & reweighting
151
+ of data for the new current year if self._aging_data is True.
152
152
  """
153
153
  # move to next year
154
154
  self.__current_year += 1
taxcalc/parameters.py CHANGED
@@ -706,11 +706,6 @@ class Parameters(pt.Parameters):
706
706
  """Propery docstring"""
707
707
  return self.end_year - self.start_year + 1
708
708
 
709
- @property
710
- def parameter_warnings(self):
711
- """Propery docstring"""
712
- return self.errors or {}
713
-
714
709
  @property
715
710
  def parameter_errors(self):
716
711
  """Propery docstring"""
@@ -76,8 +76,8 @@
76
76
  }
77
77
  },
78
78
  "parameter_indexing_CPI_offset": {
79
- "title": "OBSOLETE: Decimal offset ADDED to unchained CPI to get parameter indexing rate",
80
- "description": "OBSOLETE policy parameter.",
79
+ "title": "Decimal offset added to chained CPI-U inflation rate to get policy parameter indexing rate",
80
+ "description": "Always zero in policy_current_law.json, but non-zero values are allowed in reforms.",
81
81
  "notes": "",
82
82
  "section_1": "Parameter Indexing",
83
83
  "section_2": "Offsets",
@@ -104,6 +104,10 @@
104
104
  {
105
105
  "year": 2017,
106
106
  "value": 0.0
107
+ },
108
+ {
109
+ "year": 2018,
110
+ "value": 0.0
107
111
  }
108
112
  ],
109
113
  "validators": {
taxcalc/taxcalcio.py CHANGED
@@ -115,23 +115,37 @@ class TaxCalcIO():
115
115
  else:
116
116
  msg = 'INPUT is neither string nor Pandas DataFrame'
117
117
  self.errmsg += f'ERROR: {msg}\n'
118
- # check name and existence of BASELINE file
118
+ # check name(s) and existence of BASELINE file(s)
119
119
  bas = '-x'
120
120
  if baseline is None:
121
+ self.specified_baseline = False
121
122
  bas = '-#'
122
123
  elif isinstance(baseline, str):
123
- # remove any leading directory path from BASELINE filename
124
- fname = os.path.basename(baseline)
125
- # check if fname ends with ".json"
126
- if fname.endswith('.json'):
127
- bas = f'-{fname[:-5]}'
128
- else:
129
- msg = 'BASELINE file name does not end in .json'
130
- self.errmsg += f'ERROR: {msg}\n'
131
- # check existence of BASELINE file
132
- if not os.path.isfile(baseline):
133
- msg = 'BASELINE file could not be found'
134
- self.errmsg += f'ERROR: {msg}\n'
124
+ self.specified_baseline = True
125
+ # split any compound baseline into list of simple reforms
126
+ basnames = []
127
+ baselines = baseline.split('+')
128
+ for bas in baselines:
129
+ # remove any leading directory path from bas filename
130
+ fname = os.path.basename(bas)
131
+ # check if fname ends with ".json"
132
+ if not fname.endswith('.json'):
133
+ msg = f'{fname} does not end in .json'
134
+ self.errmsg += f'ERROR: BASELINE file name {msg}\n'
135
+ # check existence of BASELINE file
136
+ if not os.path.isfile(bas):
137
+ msg = f'{bas} could not be found'
138
+ self.errmsg += f'ERROR: BASELINE file {msg}\n'
139
+ # add fname to list of basnames used in output file names
140
+ basnames.append(fname)
141
+ # create (possibly compound) baseline name for output file names
142
+ bas = '-'
143
+ num_basnames = 0
144
+ for basname in basnames:
145
+ num_basnames += 1
146
+ if num_basnames > 1:
147
+ bas += '+'
148
+ bas += f'{basname[:-5]}'
135
149
  else:
136
150
  msg = 'TaxCalcIO.ctor: baseline is neither None nor str'
137
151
  self.errmsg += f'ERROR: {msg}\n'
@@ -191,6 +205,15 @@ class TaxCalcIO():
191
205
  self.errmsg += f'ERROR: {msg}\n'
192
206
  # create OUTPUT file name and delete any existing output files
193
207
  self.output_filename = f'{inp}{bas}{ref}{asm}.xxx'
208
+ self.delete_output_files()
209
+ # initialize variables whose values are set in init method
210
+ self.calc_ref = None
211
+ self.calc_bas = None
212
+
213
+ def delete_output_files(self):
214
+ """
215
+ Delete all output files derived from self.output_filename.
216
+ """
194
217
  extensions = [
195
218
  '-params.bas',
196
219
  '-params.ref',
@@ -202,9 +225,6 @@ class TaxCalcIO():
202
225
  ]
203
226
  for ext in extensions:
204
227
  delete_file(self.output_filename.replace('.xxx', ext))
205
- # initialize variables whose values are set in init method
206
- self.calc_ref = None
207
- self.calc_bas = None
208
228
 
209
229
  def init(self, input_data, tax_year, baseline, reform, assump,
210
230
  aging_input_data, exact_calculations):
@@ -258,8 +278,12 @@ class TaxCalcIO():
258
278
  return
259
279
  # get assumption sub-dictionaries
260
280
  assumpdict = Calculator.read_json_param_objects(None, assump)
261
- # get policy parameter dictionary from --baseline file
262
- poldict_bas = Calculator.read_json_param_objects(baseline, None)
281
+ # get policy parameter dictionaries from --baseline file(s)
282
+ poldicts_bas = []
283
+ if self.specified_baseline:
284
+ for bas in baseline.split('+'):
285
+ pdict = Calculator.read_json_param_objects(bas, None)
286
+ poldicts_bas.append(pdict['policy'])
263
287
  # get policy parameter dictionaries from --reform file(s)
264
288
  poldicts_ref = []
265
289
  if self.specified_reform:
@@ -288,20 +312,29 @@ class TaxCalcIO():
288
312
  self.gf_reform = copy.deepcopy(gfactors_ref)
289
313
  # create Policy objects:
290
314
  # ... the baseline Policy object
291
- pol_bas = Policy(
292
- gfactors=gfactors_bas,
293
- last_budget_year=last_b_year,
294
- )
295
- try:
296
- pol_bas.implement_reform(
297
- poldict_bas['policy'],
298
- print_warnings=True,
299
- raise_errors=False,
315
+ if self.specified_baseline:
316
+ pol_bas = Policy(
317
+ gfactors=gfactors_bas,
318
+ last_budget_year=last_b_year,
319
+ )
320
+ for poldict in poldicts_bas:
321
+ try:
322
+ pol_bas.implement_reform(
323
+ poldict,
324
+ print_warnings=True,
325
+ raise_errors=False,
326
+ )
327
+ if self.errmsg:
328
+ self.errmsg += "\n"
329
+ for _, errors in pol_bas.parameter_errors.items():
330
+ self.errmsg += "\n".join(errors)
331
+ except paramtools.ValidationError as valerr_msg:
332
+ self.errmsg += str(valerr_msg)
333
+ else:
334
+ pol_bas = Policy(
335
+ gfactors=gfactors_bas,
336
+ last_budget_year=last_b_year,
300
337
  )
301
- for _, errors in pol_bas.parameter_errors.items():
302
- self.errmsg += "\n".join(errors)
303
- except paramtools.ValidationError as valerr_msg:
304
- self.errmsg += str(valerr_msg)
305
338
  # ... the reform Policy object
306
339
  if self.specified_reform:
307
340
  pol_ref = Policy(
@@ -419,6 +452,22 @@ class TaxCalcIO():
419
452
  dirpath = os.path.abspath(os.path.dirname(__file__))
420
453
  return os.path.join(dirpath, self.output_filename)
421
454
 
455
+ def advance_to_year(self, year, aging_data):
456
+ """
457
+ Update self.output_filename and advance Calculator objects to year.
458
+ """
459
+ # update self.output_filename and delete output files
460
+ parts = self.output_filename.split('-')
461
+ parts[1] = str(year)[2:]
462
+ self.output_filename = '-'.join(parts)
463
+ self.delete_output_files()
464
+ # advance baseline and reform Calculator objects to specified year
465
+ self.calc_bas.advance_to_year(year)
466
+ self.calc_ref.advance_to_year(year)
467
+ idata = 'Advance input data and ' if aging_data else 'Advance'
468
+ if not self.silent:
469
+ print(f'{idata} policy to {year}')
470
+
422
471
  def analyze(
423
472
  self,
424
473
  output_params=False,
@@ -458,47 +507,36 @@ class TaxCalcIO():
458
507
  """
459
508
  # pylint: disable=too-many-arguments,too-many-positional-arguments
460
509
  # pylint: disable=too-many-branches,too-many-locals
461
- if self.puf_input_data and self.calc_ref.reform_warnings:
462
- print( # pragma: no cover
463
- 'PARAMETER VALUE WARNING(S): '
464
- '(read documentation for each parameter)\n'
465
- f'{self.calc_ref.reform_warnings}'
466
- 'CONTINUING WITH CALCULATIONS...'
467
- )
468
- calc_base_calculated = False
510
+ doing_calcs = output_tables or output_graphs or output_dump
511
+ # optionally write --params output to text files
512
+ if output_params:
513
+ self.write_policy_params_files()
514
+ if not doing_calcs:
515
+ return
516
+ # do output calculations
517
+ self.calc_bas.calc_all()
469
518
  self.calc_ref.calc_all()
470
519
  if output_dump:
471
520
  assert isinstance(dump_varlist, list)
472
521
  assert len(dump_varlist) > 0
473
- # might need marginal tax rates
522
+ # might need marginal tax rates for dumpdb
474
523
  (mtr_ptax_ref, mtr_itax_ref,
475
524
  _) = self.calc_ref.mtr(wrt_full_compensation=False,
476
525
  calc_all_already_called=True)
477
- self.calc_bas.calc_all()
478
- calc_base_calculated = True
479
526
  (mtr_ptax_bas, mtr_itax_bas,
480
527
  _) = self.calc_bas.mtr(wrt_full_compensation=False,
481
528
  calc_all_already_called=True)
482
529
  else:
483
- # definitely do not need marginal tax rates
530
+ # do not need marginal tax rates for dumpdb
484
531
  mtr_ptax_ref = None
485
532
  mtr_itax_ref = None
486
533
  mtr_ptax_bas = None
487
534
  mtr_itax_bas = None
488
- # optionally write --params output to text files
489
- if output_params:
490
- self.write_policy_params_files()
491
535
  # optionally write --tables output to text file
492
536
  if output_tables:
493
- if not calc_base_calculated:
494
- self.calc_bas.calc_all()
495
- calc_base_calculated = True
496
537
  self.write_tables_file()
497
538
  # optionally write --graphs output to HTML files
498
539
  if output_graphs:
499
- if not calc_base_calculated:
500
- self.calc_bas.calc_all()
501
- calc_base_calculated = True
502
540
  self.write_graph_files()
503
541
  # optionally write --dumpdb output to SQLite database file
504
542
  if output_dump:
@@ -731,7 +769,7 @@ class TaxCalcIO():
731
769
 
732
770
  def dump_variables(self, dumpvars_str):
733
771
  """
734
- Return set of variable names extracted from dumpvars_str, plus
772
+ Return list of variable names extracted from dumpvars_str, plus
735
773
  minimal baseline/reform variables even if not in dumpvars_str.
736
774
  Also, builds self.errmsg if any specified variables are not valid.
737
775
  """
@@ -69,7 +69,7 @@ def test_make_calculator_with_policy_reform(cps_subsample):
69
69
  pol.implement_reform(reform)
70
70
  # create a Calculator object using this policy reform
71
71
  calc = Calculator(policy=pol, records=rec)
72
- assert calc.reform_warnings == {}
72
+ assert calc.reform_errors == {}
73
73
  # check that Policy object embedded in Calculator object is correct
74
74
  assert calc.current_year == year
75
75
  assert calc.policy_param('II_em') == 4000
@@ -28,7 +28,7 @@ def test_2017_law_reform(tests_path):
28
28
  with open(reform_file, 'r', encoding='utf-8') as rfile:
29
29
  rtext = rfile.read()
30
30
  pol.implement_reform(Policy.read_json_reform(rtext))
31
- assert not pol.parameter_warnings
31
+ assert not pol.parameter_errors
32
32
  pol.set_year(2018)
33
33
  pre_mdata = dict(pol.items())
34
34
  # check some policy parameter values against expected values under 2017 law
@@ -102,14 +102,14 @@ def test_round_trip_reforms(fyear, tests_path):
102
102
  with open(reform_file, 'r', encoding='utf-8') as rfile:
103
103
  rtext = rfile.read()
104
104
  rtr_pol.implement_reform(Policy.read_json_reform(rtext))
105
- assert not rtr_pol.parameter_warnings
105
+ assert not rtr_pol.parameter_errors
106
106
  assert not rtr_pol.errors
107
107
  # Layer on TCJA
108
108
  reform_file = os.path.join(tests_path, '..', 'reforms', 'TCJA.json')
109
109
  with open(reform_file, 'r', encoding='utf-8') as rfile:
110
110
  rtext = rfile.read()
111
111
  rtr_pol.implement_reform(Policy.read_json_reform(rtext))
112
- assert not rtr_pol.parameter_warnings
112
+ assert not rtr_pol.parameter_errors
113
113
  assert not rtr_pol.errors
114
114
  # Layer on the CARES Act
115
115
  reform_file = os.path.join(tests_path, '..', 'reforms', 'CARES.json')
@@ -123,21 +123,21 @@ def test_round_trip_reforms(fyear, tests_path):
123
123
  with open(reform_file, 'r', encoding='utf-8') as rfile:
124
124
  rtext = rfile.read()
125
125
  rtr_pol.implement_reform(Policy.read_json_reform(rtext))
126
- assert not rtr_pol.parameter_warnings
126
+ assert not rtr_pol.parameter_errors
127
127
  assert not rtr_pol.errors
128
128
  # Layer on ARPA
129
129
  reform_file = os.path.join(tests_path, '..', 'reforms', 'ARPA.json')
130
130
  with open(reform_file, 'r', encoding='utf-8') as rfile:
131
131
  rtext = rfile.read()
132
132
  rtr_pol.implement_reform(Policy.read_json_reform(rtext))
133
- assert not rtr_pol.parameter_warnings
133
+ assert not rtr_pol.parameter_errors
134
134
  assert not rtr_pol.errors
135
135
  # Layer on rounding from IRS through Policy.LAST_KNOWN_YEAR
136
136
  reform_file = os.path.join(tests_path, '..', 'reforms', 'rounding.json')
137
137
  with open(reform_file, 'r', encoding='utf-8') as rfile:
138
138
  rtext = rfile.read()
139
139
  rtr_pol.implement_reform(Policy.read_json_reform(rtext))
140
- assert not rtr_pol.parameter_warnings
140
+ assert not rtr_pol.parameter_errors
141
141
  assert not rtr_pol.errors
142
142
  rtr_pol.set_year(fyear)
143
143
  rtr_mdata = dict(rtr_pol.items())
@@ -371,7 +371,7 @@ def test_ext_reform(tests_path):
371
371
  with open(reform_file, 'r', encoding='utf-8') as rfile:
372
372
  rtext = rfile.read()
373
373
  ext.implement_reform(Policy.read_json_reform(rtext))
374
- assert not ext.parameter_warnings
374
+ assert not ext.parameter_errors
375
375
  ext.set_year(2026)
376
376
  assert ext.II_em < end.II_em
377
377
  # test tax output generated by ext.json reform file using public CPS data
@@ -357,6 +357,10 @@ def test_ctor_init_with_cps_files():
357
357
  exact_calculations=False)
358
358
  assert not tcio.errmsg
359
359
  assert tcio.tax_year() == txyr
360
+ # test advance_to_year method
361
+ tcio.silent = False
362
+ tcio.advance_to_year(txyr + 1, True)
363
+ assert tcio.tax_year() == txyr + 1
360
364
  # specify invalid tax_year for cps.csv input data
361
365
  txyr = 2013
362
366
  tcio = TaxCalcIO('cps.csv', txyr, None, None, None)
@@ -484,14 +488,14 @@ def test_write_policy_param_files(reformfile1):
484
488
  tcio = TaxCalcIO(
485
489
  input_data=pd.read_csv(StringIO(RAWINPUT)),
486
490
  tax_year=taxyear,
487
- baseline=None,
491
+ baseline=compound_reform,
488
492
  reform=compound_reform,
489
493
  assump=None,
490
494
  )
491
495
  assert not tcio.errmsg
492
496
  tcio.init(input_data=pd.read_csv(StringIO(RAWINPUT)),
493
497
  tax_year=taxyear,
494
- baseline=None,
498
+ baseline=compound_reform,
495
499
  reform=compound_reform,
496
500
  assump=None,
497
501
  aging_input_data=False,
@@ -539,19 +543,7 @@ def test_no_tables_or_graphs(reformfile1):
539
543
  output_tables=True,
540
544
  output_graphs=True)
541
545
  # delete tables and graph files
542
- output_filename = tcio.output_filepath()
543
- extensions = [
544
- '-params.bas',
545
- '-params.ref',
546
- '-tables.text',
547
- '-atr.html',
548
- '-mtr.html',
549
- '-pch.html',
550
- ]
551
- for ext in extensions:
552
- fname = output_filename.replace('.xxx', ext)
553
- if os.path.isfile(fname):
554
- os.remove(fname)
546
+ tcio.delete_output_files()
555
547
 
556
548
 
557
549
  def test_tables(reformfile1):
@@ -584,11 +576,7 @@ def test_tables(reformfile1):
584
576
  assert not tcio.errmsg
585
577
  # create TaxCalcIO tables file
586
578
  tcio.analyze(output_tables=True)
587
- # delete tables file
588
- output_filename = tcio.output_filepath()
589
- fname = output_filename.replace('.xxx', '-tables.text')
590
- if os.path.isfile(fname):
591
- os.remove(fname)
579
+ tcio.delete_output_files()
592
580
 
593
581
 
594
582
  def test_graphs(reformfile1):
@@ -622,16 +610,7 @@ def test_graphs(reformfile1):
622
610
  assert not tcio.errmsg
623
611
  tcio.analyze(output_graphs=True)
624
612
  # delete graph files
625
- output_filename = tcio.output_filepath()
626
- fname = output_filename.replace('.xxx', '-atr.html')
627
- if os.path.isfile(fname):
628
- os.remove(fname)
629
- fname = output_filename.replace('.xxx', '-mtr.html')
630
- if os.path.isfile(fname):
631
- os.remove(fname)
632
- fname = output_filename.replace('.xxx', '-pch.html')
633
- if os.path.isfile(fname):
634
- os.remove(fname)
613
+ tcio.delete_output_files()
635
614
 
636
615
 
637
616
  @pytest.fixture(scope='session', name='warnreformfile')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taxcalc
3
- Version: 4.6.0
3
+ Version: 4.6.1
4
4
  Summary: Tax-Calculator
5
5
  Home-page: https://github.com/PSLmodels/Tax-Calculator
6
6
  Download-URL: https://github.com/PSLmodels/Tax-Calculator
@@ -1,32 +1,32 @@
1
- taxcalc/__init__.py,sha256=jQUxALCvW-PLnHIR91_Qjnd0_TULCiULAKJGbNrAc6c,536
1
+ taxcalc/__init__.py,sha256=_SNXt6U2NMWDx1hqcUw-N6_SVHp4pydp4-U1VFEPldY,536
2
2
  taxcalc/calcfunctions.py,sha256=G62sQeW-YHJ2gvqOfidRKPBpufSHzXYIdI2PGcApOuU,148423
3
- taxcalc/calculator.py,sha256=otaljKtkO91uWH6QqChsR17xjLXcHJ2QEvyoMnYeung,63936
3
+ taxcalc/calculator.py,sha256=cCsis9yQSyw4eXvYppk7Z70JEaPH8Kd1AADlIqnJhVw,63930
4
4
  taxcalc/conftest.py,sha256=nO4J7qu1sTHgjqrzhpRMvfMJUrNm6GP_IsSuuDt_MeQ,141
5
5
  taxcalc/consumption.json,sha256=FBgDd72AZnviQRhGz5rPgpGpOmaOzNYfGB9GdTCeKPI,8102
6
6
  taxcalc/consumption.py,sha256=pkXhFGpFqu7hW62KaTctfRSzR-pXzMB1ai8XCQRAgXk,3480
7
7
  taxcalc/cps.csv.gz,sha256=SS6tSduU_Eu0EJwzpslnmqMsQQQucVMzzITfH-SeV40,9851074
8
8
  taxcalc/cps_weights.csv.gz,sha256=-k31Swqss0WEGA3Zo8AoReLR_C7BRUk4PDmfh-oVVyo,13657706
9
- taxcalc/data.py,sha256=ERqmDQFi3yrl1uLvPs1C1tZN-711nsP8PyStVKU378g,11679
9
+ taxcalc/data.py,sha256=QRyLsq250OsxUIjsuCFtREJsVyEOGaivwcON3teivMI,11698
10
10
  taxcalc/decorators.py,sha256=EwyVH4fSDf8x_BHzccf4-DRopo6KMVidYSYZFy_dJy0,11307
11
11
  taxcalc/growdiff.json,sha256=ReBAF6We9ZwTb0uDYQwbedPsQKdm6KSOERiddEYZbm0,15037
12
12
  taxcalc/growdiff.py,sha256=Q3St-KPIUN2I_l1S0jwN0yr8O4LuzkNIU-_qbXTkrZw,2977
13
13
  taxcalc/growfactors.csv,sha256=URIGSKApCY4McvdILkCaIm8EhCGEME2Du-ef5Q9c0uE,5134
14
14
  taxcalc/growfactors.py,sha256=dW71yYKhFDbFtGpBT-kZBqY4MV7IswKcNI3_c-X_Nfg,6525
15
- taxcalc/parameters.py,sha256=pvr3m4G-fA_d-LMqCNYdBtJDjkDcHLgOgw0kmchYhFk,33235
15
+ taxcalc/parameters.py,sha256=pxW53fOJkW7yM7XxN04RDNs-f7CwKOjE4vMmMvPtZ8E,33121
16
16
  taxcalc/policy.py,sha256=4bY3beSYhLKk_X4CTgG7LGnVnILM0D4Invt7uCp8dlA,6725
17
- taxcalc/policy_current_law.json,sha256=dqfeSqdYe-XUAdZOS7dccUEA48d80JqwnI34nsHbt_g,689758
17
+ taxcalc/policy_current_law.json,sha256=enemNPLg0ZlwNQVVK51XJru_YzDzPnV7ClNhdJHlhJQ,689915
18
18
  taxcalc/puf_ratios.csv,sha256=USMM79UEX8PnSKY_LYLxvACER_9NHyj4ipNEk2iyykQ,3580
19
19
  taxcalc/puf_weights.csv.gz,sha256=35iZhzFO0dHUI2zf4_liyk60E-3Sgr_AbxszGxw7LfM,13522484
20
20
  taxcalc/records.py,sha256=K5QgP6Pm9PtEVDVzM7tpHYPu554Wio0KnrU7YTTBKgQ,18264
21
21
  taxcalc/records_variables.json,sha256=_YKJiZQ1czp0VH9bkr5ZXmp9Mm1roHoHrt2XnLXAtfw,42975
22
- taxcalc/taxcalcio.py,sha256=F-MNNkIVYdOCfFBE1nKOajoS8c2MjkSQ0aKssyDXMh4,33188
22
+ taxcalc/taxcalcio.py,sha256=1xZ17wZ0yqZeDDa5glEQpCG6xvXJ4wGxbUQPkmTLTD0,34752
23
23
  taxcalc/utils.py,sha256=noHnQ-tw0AXhEH-OgdTUiPedH8luVOJ5NdfpKI3_obw,62401
24
24
  taxcalc/utilsprvt.py,sha256=iIyWp9-N3_XWjQj2jV2CWnJy7vrNlKB2_vIMwYjgbWY,1323
25
25
  taxcalc/assumptions/ASSUMPTIONS.md,sha256=cFQqWn1nScaladVaQ7xNm1jDY8CsGdLmqZEzUZeRrb8,1917
26
26
  taxcalc/assumptions/README.md,sha256=Ww55r2zH1neoRSl_MawrPmX-ugaztIZ7_ALrquuatdQ,809
27
27
  taxcalc/assumptions/economic_assumptions_template.json,sha256=utMk38GwSQFrkOAtRrDVhMQLpfaZH3JmtTznKX7IouM,2610
28
28
  taxcalc/cli/__init__.py,sha256=cyZ0tdx41j_vV_B6GAkqJmNKKG-B0wUC0ThC75lJlk4,104
29
- taxcalc/cli/tc.py,sha256=rJ5VTxyqUn7CVG3fXmTIHKdvL7lEfCFw1xHlFtRDZv8,14400
29
+ taxcalc/cli/tc.py,sha256=StZo-46v1c3ozP-64wQWz0uyHOEVqZPAzgTr1JuoFjU,16614
30
30
  taxcalc/reforms/2017_law.json,sha256=u-xaPSvt5ubfZw1Nb-jNhTNcOPBUJeAX2kJVoEyMgC4,5860
31
31
  taxcalc/reforms/2017_law.out.csv,sha256=nnNKXqY2kof8HC1nnU8srPsvNNepi6ISXQ9OJpQnT7M,473
32
32
  taxcalc/reforms/ARPA.json,sha256=1H9LuJ_QitXRO9e8R3PWizajJgdioIzbGFdvdlt9FVg,3119
@@ -86,7 +86,7 @@ taxcalc/tests/reforms_expect.csv,sha256=6m0bSH8S6ReMYdHoQC9iWYuJAof5sZ0OONVBi7zX
86
86
  taxcalc/tests/test_4package.py,sha256=I9i5rWFsg-VCtTLmUWIi9dnusieTZeodrgH8ba_Fbng,3681
87
87
  taxcalc/tests/test_benefits.py,sha256=oaui5mO0TuW8Ht-uxvUCBL5zM3iTENq3Whyf_gEpY1U,3392
88
88
  taxcalc/tests/test_calcfunctions.py,sha256=QtMgQg_WpCU8PZM3Hs8Pml4q6LnlLWV_blCzbpGN2sw,31939
89
- taxcalc/tests/test_calculator.py,sha256=dumYsleaKjW8zD1VUnmyQbuszf96QEqXAMf8dIvtelM,36087
89
+ taxcalc/tests/test_calculator.py,sha256=lcnBli5giNFxqgCLW-MF4J_wsV3gSFqCjzJdCJkihek,36085
90
90
  taxcalc/tests/test_compare.py,sha256=WglH4ZJOEQnav_mAmnGjpb7dooXcQsB1m04hgQeoBUQ,10873
91
91
  taxcalc/tests/test_compatible_data.py,sha256=XpfnE96HDvlgJM_8KEasgufxeiePTFfNMBBVlfzHP9Y,13477
92
92
  taxcalc/tests/test_consumption.py,sha256=c6zi9GqNK54cgfzxiWmY10UxOMJZ9vd9fOTwUKU1zMk,6318
@@ -100,9 +100,9 @@ taxcalc/tests/test_policy.py,sha256=hJrFqqptMoUWk3abIJ2sdADQtCqvGaUeW5as1aNwhR8,
100
100
  taxcalc/tests/test_puf_var_stats.py,sha256=TG-sQtOkR6cBOw0am7MUsMSwjPxVdkvt0Xov342oii8,7786
101
101
  taxcalc/tests/test_pufcsv.py,sha256=kQPwpMcLNPwQDiO-23xvvZggtHYvRz-UYEDn6JjNfbw,16352
102
102
  taxcalc/tests/test_records.py,sha256=ncswnbCY7vZgJ_h6xwql4oFw-jZG2gWOMWvEJC2_jdc,8827
103
- taxcalc/tests/test_reforms.py,sha256=IGMAzVqAMJqdnDDRsMs3VZZOV9_o4gsBAyYgyPB2m2M,16047
103
+ taxcalc/tests/test_reforms.py,sha256=djVxMOE2RytmDDYvzS55AnThRRBcVc_calPbfHuvQ-k,16033
104
104
  taxcalc/tests/test_responses.py,sha256=2CkVVdaDNCSALMoUcGgweRlS2tfsK3kXogHtDkZMqJU,1663
105
- taxcalc/tests/test_taxcalcio.py,sha256=Ee7qfUI-Gy_lbFPElOfOaFtIZ_0NboxVXK0Wrxl9BBw,23794
105
+ taxcalc/tests/test_taxcalcio.py,sha256=PV7hCntwp_1LHQYlft7WelbzKSoi5aWzUZqhJAcC5AE,23135
106
106
  taxcalc/tests/test_utils.py,sha256=aUHVNxfSMdyyLAz3w98KLQa5D872rZxXqKbPkIqwLLA,29403
107
107
  taxcalc/validation/CSV_INPUT_VARS.md,sha256=MqlZZGt_a1n8JAU-nY5MjnTmjz1pMOuhtpVYIGUgl38,1433
108
108
  taxcalc/validation/CSV_OUTPUT_VARS.md,sha256=wr8oyCJDXcxl4Lu0H_wMofUQYhEIyHDif6vkbas1FGE,3000
@@ -131,9 +131,9 @@ taxcalc/validation/taxsim35/expected_differences/b21-taxdiffs-expect.csv,sha256=
131
131
  taxcalc/validation/taxsim35/expected_differences/c17-taxdiffs-expect.csv,sha256=YhgojbLowH3yujdYu7SGkdvBZmTgpugu4wYc1Be069M,1125
132
132
  taxcalc/validation/taxsim35/expected_differences/c18-taxdiffs-expect.csv,sha256=g9J4BPbTySV-h-RcLvReJq9v1jscgiRSSZzi0taEA-k,1225
133
133
  taxcalc/validation/taxsim35/expected_differences/c19-taxdiffs-expect.csv,sha256=Ceh15N_Xr3L7cpYjzGa-8NLCV3obc8PNHEhE5ZxSPhI,1238
134
- taxcalc-4.6.0.dist-info/licenses/LICENSE,sha256=m5epLdB-_NXiY7NsEDgcHP4jDtJ4vOlRf5S3Jb-jraY,1299
135
- taxcalc-4.6.0.dist-info/METADATA,sha256=3sgO2tiOEtszzhDVjXJgEqx97l3MGXWsKPUNjm1oSuY,3509
136
- taxcalc-4.6.0.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
137
- taxcalc-4.6.0.dist-info/entry_points.txt,sha256=a3ZE1piRv683p27fOLdWZvVJXESkoslTOp5iXV7uVco,50
138
- taxcalc-4.6.0.dist-info/top_level.txt,sha256=Wh8wTDHkA_cm4dn8IoUCviDyGgVQqwEQKPJnl8z6d4c,8
139
- taxcalc-4.6.0.dist-info/RECORD,,
134
+ taxcalc-4.6.1.dist-info/licenses/LICENSE,sha256=m5epLdB-_NXiY7NsEDgcHP4jDtJ4vOlRf5S3Jb-jraY,1299
135
+ taxcalc-4.6.1.dist-info/METADATA,sha256=wvnDftJBQ7SKgJAF-hCYRKf_RJX3RNSPj2LLe-nL59s,3509
136
+ taxcalc-4.6.1.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
137
+ taxcalc-4.6.1.dist-info/entry_points.txt,sha256=a3ZE1piRv683p27fOLdWZvVJXESkoslTOp5iXV7uVco,50
138
+ taxcalc-4.6.1.dist-info/top_level.txt,sha256=Wh8wTDHkA_cm4dn8IoUCviDyGgVQqwEQKPJnl8z6d4c,8
139
+ taxcalc-4.6.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.0.0)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5