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
taxcalc/parameters.py CHANGED
@@ -1,13 +1,14 @@
1
+ """
2
+ Tax-Calculator abstract base parameter class based on paramtools package.
3
+ """
4
+
1
5
  import os
2
6
  import copy
3
7
  from collections import defaultdict
4
8
  from typing import Union, Mapping, Any, List
5
-
9
+ import numpy as np
6
10
  import marshmallow as ma
7
11
  import paramtools as pt
8
- import numpy as np
9
-
10
- from taxcalc.growfactors import GrowFactors
11
12
 
12
13
 
13
14
  class CompatibleDataSchema(ma.Schema):
@@ -64,8 +65,8 @@ class Parameters(pt.Parameters):
64
65
  Check out the ParamTools
65
66
  `documentation <https://paramtools.dev/api/reference.html>`_
66
67
  for more information on these inherited methods.
67
-
68
68
  """
69
+ # pylint: disable=too-many-instance-attributes
69
70
  defaults = None
70
71
  array_first = True
71
72
  label_to_extend = "year"
@@ -83,6 +84,8 @@ class Parameters(pt.Parameters):
83
84
 
84
85
  def __init__(self, start_year=None, num_years=None, last_known_year=None,
85
86
  removed=None, redefined=None, wage_indexed=None, **kwargs):
87
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
88
+
86
89
  # In case we need to wait for this to be called from the
87
90
  # initialize method for legacy reasons.
88
91
  if not start_year or not num_years:
@@ -121,8 +124,12 @@ class Parameters(pt.Parameters):
121
124
  label["validators"]["range"]["max"] = last_budget_year
122
125
  super().__init__(**kwargs)
123
126
 
124
- def adjust(
125
- self, params_or_path, print_warnings=True, raise_errors=True, **kwargs
127
+ def adjust( # pylint: disable=arguments-differ
128
+ # pylint warning W0221 is:
129
+ # Number of parameters was 6 in 'Parameters.adjust' and
130
+ # is now 5 in overriding 'Parameters.adjust' method
131
+ self, params_or_path,
132
+ print_warnings=True, raise_errors=True, **kwargs
126
133
  ):
127
134
  """
128
135
  Update parameter values using a ParamTools styled adjustment.
@@ -136,8 +143,10 @@ class Parameters(pt.Parameters):
136
143
 
137
144
  {
138
145
  "standard_deduction": [
139
- {"year": 2024, "marital_status": "single", "value": 10000.0},
140
- {"year": 2024, "marital_status": "joint", "value": 10000.0}
146
+ {"year": 2024, "marital_status": "single",
147
+ "value": 10000.0},
148
+ {"year": 2024, "marital_status": "joint",
149
+ "value": 10000.0}
141
150
  ],
142
151
  "ss_rate": [{"year": 2024, "value": 0.2}]}
143
152
  }
@@ -154,7 +163,7 @@ class Parameters(pt.Parameters):
154
163
  adjustment : Dict
155
164
  Parsed paremeter dictionary
156
165
 
157
- """ # noqa
166
+ """
158
167
  if print_warnings:
159
168
  _data = copy.deepcopy(self._data)
160
169
  kwargs["ignore_warnings"] = False
@@ -177,13 +186,15 @@ class Parameters(pt.Parameters):
177
186
  except pt.ValidationError as ve:
178
187
  if self.errors and raise_errors:
179
188
  raise ve
180
- elif self.errors and not raise_errors:
189
+ if self.errors and not raise_errors:
181
190
  return {}
182
191
  if print_warnings:
183
192
  print("WARNING:")
184
193
  print(self.warnings)
185
194
  kwargs["ignore_warnings"] = True
195
+ # pylint: disable=possibly-used-before-assignment
186
196
  self._data = _data
197
+ # pylint: enable=possibly-used-before-assignment
187
198
  _warnings = copy.deepcopy(self._warnings)
188
199
  self._warnings = {}
189
200
  self._errors = {}
@@ -242,6 +253,8 @@ class Parameters(pt.Parameters):
242
253
  wiped out after the year in which the value is adjusted for the
243
254
  same hard-coding reason.
244
255
  """
256
+ # pylint: disable=too-many-statements,too-many-locals,too-many-branches
257
+
245
258
  # Temporarily turn off extra ops during the intermediary adjustments
246
259
  # so that expensive and unnecessary operations are not run.
247
260
  label_to_extend = self.label_to_extend
@@ -310,7 +323,7 @@ class Parameters(pt.Parameters):
310
323
  self.delete(to_delete, **kwargs)
311
324
 
312
325
  # 1.b For all others, these are years after last_known_year.
313
- last_known_year = max(cpi_min_year["year"], self._last_known_year)
326
+ # last_known_year=max(cpi_min_year["year"],self._last_known_year)
314
327
  # calculate 2026 value, using new inflation rates, for parameters
315
328
  # that revert to their pre-TCJA values.
316
329
  long_params = ['II_brk7', 'II_brk6', 'II_brk5', 'II_brk4',
@@ -362,7 +375,15 @@ class Parameters(pt.Parameters):
362
375
  ):
363
376
  continue
364
377
  if self._data[param].get("indexed", False):
365
- to_delete[param] = self.sel[param]["_auto"] == True # noqa
378
+ # pylint: disable=singleton-comparison
379
+ to_delete[param] = self.sel[param]["_auto"] == True
380
+ # pylint warning message:
381
+ # Comparison 'self.sel[param]['_auto'] == True' should
382
+ # be 'self.sel[param]['_auto'] is True' if checking for
383
+ # the singleton value True, or
384
+ # 'bool(self.sel[param]['_auto'])' if testing for
385
+ # truthiness
386
+ # pylint: enable=singleton-comparison
366
387
  needs_reset.append(param)
367
388
 
368
389
  self.delete(to_delete, **kwargs)
@@ -405,13 +426,14 @@ class Parameters(pt.Parameters):
405
426
  min_index_change_year, strict=False
406
427
  )
407
428
 
408
- if len(list(vos)):
429
+ if list(vos):
409
430
  min_adj_year = min(vos, key=lambda vo: vo["year"])[
410
431
  "year"
411
432
  ]
412
433
  self.delete(
413
434
  {
414
- base_param: self.sel[base_param]["year"] > min_adj_year # noqa
435
+ base_param:
436
+ self.sel[base_param]["year"] > min_adj_year
415
437
  }
416
438
  )
417
439
  super().adjust({base_param: vos}, **kwargs)
@@ -482,40 +504,45 @@ class Parameters(pt.Parameters):
482
504
  )
483
505
  return adj
484
506
 
485
- def get_index_rate(self, param, label_to_extend_val):
507
+ def get_index_rate(self, param, lte_val):
486
508
  """
487
509
  Initalize indexing data and return the indexing rate value
488
- depending on the parameter name and label_to_extend_val, the value of
489
- label_to_extend.
510
+ depending on the parameter name and lte_val (that is, the
511
+ label_to_extend_val), the value of label_to_extend.
490
512
  Returns: rate to use for indexing.
491
513
  """
492
514
  if not self._inflation_rates or not self._wage_growth_rates:
493
515
  self.set_rates()
494
516
  if param in self._wage_indexed:
495
- return self.wage_growth_rates(year=label_to_extend_val)
496
- else:
497
- return self.inflation_rates(year=label_to_extend_val)
517
+ return self.wage_growth_rates(year=lte_val)
518
+ return self.inflation_rates(year=lte_val)
498
519
 
499
520
  def set_rates(self):
500
521
  """
501
- This method is implemented by classes inheriting
502
- Parameters.
522
+ This method is implemented by classes inheriting Parameters.
503
523
  """
504
524
  raise NotImplementedError()
505
525
 
506
526
  def wage_growth_rates(self, year=None):
527
+ """
528
+ Return wage growth rates used in parameter indexing.
529
+ """
507
530
  if year is not None:
508
531
  syr = max(self.start_year, self._gfactors.first_year)
509
532
  return self._wage_growth_rates[year - syr]
510
533
  return self._wage_growth_rates or []
511
534
 
512
535
  def inflation_rates(self, year=None):
536
+ """
537
+ Return price inflation rates used in parameter indexing.
538
+ """
513
539
  if year is not None:
514
540
  syr = max(self.start_year, self._gfactors.first_year)
515
541
  return self._inflation_rates[year - syr]
516
542
  return self._inflation_rates or []
517
543
 
518
- # alias methods below
544
+ # alias methods below:
545
+
519
546
  def initialize(self, start_year, num_years, last_known_year=None,
520
547
  removed=None, redefined=None, wage_indexed=None,
521
548
  **kwargs):
@@ -523,13 +550,15 @@ class Parameters(pt.Parameters):
523
550
  Legacy method for initializing a Parameters instance. Projects
524
551
  should use the __init__ method in the future.
525
552
  """
526
- # case where project hasn't been initialized yet.
553
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
554
+ # Handle case where project hasn't been initialized yet
527
555
  if getattr(self, "_data", None) is None:
528
556
  return Parameters.__init__(
529
557
  self, start_year, num_years, last_known_year=last_known_year,
530
558
  removed=removed, redefined=redefined,
531
559
  wage_indexed=wage_indexed, **kwargs
532
560
  )
561
+ return None # pragma: no cover
533
562
 
534
563
  def _update(self, revision, print_warnings, raise_errors):
535
564
  """
@@ -553,13 +582,16 @@ class Parameters(pt.Parameters):
553
582
 
554
583
  {
555
584
  "standard_deduction": [
556
- {"year": 2024, "marital_status": "single", "value": 10000.0},
557
- {"year": 2024, "marital_status": "joint", "value": 10000.0}
585
+ {"year": 2024, "marital_status": "single",
586
+ "value": 10000.0},
587
+ {"year": 2024, "marital_status": "joint",
588
+ "value": 10000.0}
558
589
  ],
559
590
  "ss_rate": [{"year": 2024, "value": 0.2}]}
560
591
  }
561
592
 
562
- """ # noqa: E501
593
+ """
594
+ # pylint: disable=too-many-branches
563
595
  if not isinstance(revision, dict):
564
596
  raise pt.ValidationError(
565
597
  {"errors": {"schema": "Revision must be a dictionary."}},
@@ -651,30 +683,37 @@ class Parameters(pt.Parameters):
651
683
  )
652
684
 
653
685
  def set_year(self, year):
686
+ """Specify parameter year"""
654
687
  self.set_state(year=year)
655
688
 
656
689
  @property
657
690
  def current_year(self):
691
+ """Propery docstring"""
658
692
  return self.label_grid["year"][0]
659
693
 
660
694
  @property
661
695
  def start_year(self):
696
+ """Propery docstring"""
662
697
  return self._stateless_label_grid["year"][0]
663
698
 
664
699
  @property
665
700
  def end_year(self):
701
+ """Propery docstring"""
666
702
  return self._stateless_label_grid["year"][-1]
667
703
 
668
704
  @property
669
705
  def num_years(self):
706
+ """Propery docstring"""
670
707
  return self.end_year - self.start_year + 1
671
708
 
672
709
  @property
673
710
  def parameter_warnings(self):
711
+ """Propery docstring"""
674
712
  return self.errors or {}
675
713
 
676
714
  @property
677
715
  def parameter_errors(self):
716
+ """Propery docstring"""
678
717
  return self.errors or {}
679
718
 
680
719
  @staticmethod
@@ -698,14 +737,17 @@ class Parameters(pt.Parameters):
698
737
 
699
738
  Some examples of valid links are:
700
739
 
701
- - HTTP: ``https://raw.githubusercontent.com/PSLmodels/Tax-Calculator/master/taxcalc/reforms/2017_law.json``
740
+ - HTTP: ``https://raw.githubusercontent.com/PSLmodels/Tax-Calculator/
741
+ master/taxcalc/reforms/2017_law.json``
702
742
 
703
- - Github API: ``github://PSLmodels:Tax-Calculator@master/taxcalc/reforms/2017_law.json``
743
+ - Github API: ``github://PSLmodels:Tax-Calculator@master/taxcalc/
744
+ reforms/2017_law.json``
704
745
 
705
746
  Checkout the ParamTools
706
- `docs <https://paramtools.dev/_modules/paramtools/parameters.html#Parameters.read_params>`_
747
+ `docs <https://paramtools.dev/_modules/paramtools/
748
+ parameters.html#Parameters.read_params>`_
707
749
  for more information on valid file URLs.
708
- """ # noqa
750
+ """
709
751
  # embedded function used only in _read_json_revision staticmethod
710
752
  def convert_year_to_int(syr_dict):
711
753
  """
@@ -713,10 +755,10 @@ class Parameters(pt.Parameters):
713
755
  keys, into a dictionary with the same structure but having integer
714
756
  years as secondary keys.
715
757
  """
716
- iyr_dict = dict()
758
+ iyr_dict = {}
717
759
  for pkey, sdict in syr_dict.items():
718
760
  assert isinstance(pkey, str)
719
- iyr_dict[pkey] = dict()
761
+ iyr_dict[pkey] = {}
720
762
  assert isinstance(sdict, dict)
721
763
  for skey, val in sdict.items():
722
764
  assert isinstance(skey, str)
@@ -726,7 +768,7 @@ class Parameters(pt.Parameters):
726
768
  # end of embedded function
727
769
  # process the main function arguments
728
770
  if obj is None:
729
- return dict()
771
+ return {}
730
772
 
731
773
  full_dict = pt.read_json(obj)
732
774
 
@@ -743,6 +785,9 @@ class Parameters(pt.Parameters):
743
785
  return convert_year_to_int(single_dict)
744
786
 
745
787
  def metadata(self):
788
+ """
789
+ Return parameter specification.
790
+ """
746
791
  return self.specification(meta_data=True, use_state=False)
747
792
 
748
793
  @staticmethod
@@ -752,7 +797,7 @@ class Parameters(pt.Parameters):
752
797
  assumed to have a param:year:value format.
753
798
  """
754
799
  assert isinstance(revision, dict)
755
- years = list()
800
+ years = []
756
801
  for _, paramdata in revision.items():
757
802
  assert isinstance(paramdata, dict)
758
803
  for year, _ in paramdata.items():
@@ -774,8 +819,7 @@ class Parameters(pt.Parameters):
774
819
  return self.to_array(
775
820
  attr[1:], year=list(range(self.start_year, self.end_year + 1))
776
821
  )
777
- else:
778
- raise AttributeError(f"{attr} is not defined.")
822
+ raise AttributeError(f"{attr} is not defined.")
779
823
 
780
824
 
781
825
  TaxcalcReform = Union[str, Mapping[int, Any]]
@@ -815,7 +859,6 @@ def is_paramtools_format(params: Union[TaxcalcReform, ParamToolsAdjustment]):
815
859
  for data in params.values():
816
860
  if isinstance(data, dict):
817
861
  return False # taxcalc reform
818
- else:
819
- # Not doing a specific check to see if the value is a list
820
- # since it could be a list or just a scalar value.
821
- return True
862
+ # Not doing a specific check to see if the value is a list
863
+ # since it could be a list or just a scalar value.
864
+ return True
taxcalc/policy.py CHANGED
@@ -158,7 +158,7 @@ class Policy(Parameters):
158
158
  Policy.DEFAULTS_FILE_PATH,
159
159
  Policy.DEFAULTS_FILE_NAME
160
160
  )
161
- with open(path, 'r', encoding='utf-8') as f:
161
+ with open(path, 'r', encoding='utf-8') as f:
162
162
  defaults = json.loads(f.read()) # pylint: disable=protected-access
163
163
  return [k for k in defaults if k != "schema"]
164
164
 
taxcalc/records.py CHANGED
@@ -136,6 +136,7 @@ class Records(Data):
136
136
  adjust_ratios=PUF_RATIOS_FILENAME,
137
137
  exact_calculations=False,
138
138
  weights_scale=0.01):
139
+ # pylint: disable=too-many-positional-arguments
139
140
  # pylint: disable=no-member,too-many-branches
140
141
  if isinstance(weights, str):
141
142
  weights = os.path.join(Records.CODE_PATH, weights)
@@ -842,6 +842,12 @@
842
842
  "form": {"2013-2013": "1040 line 46 minus 1040 line 55",
843
843
  "2014-2016": "1040 line 47 minus 1040 line 56"}
844
844
  },
845
+ "c32800": {
846
+ "type": "float",
847
+ "desc": "Child and dependent care expenses capped by policy (not by earnings)",
848
+ "form": {"2013-2013": "1040 line 48",
849
+ "2014-2016": "1040 line 49"}
850
+ },
845
851
  "c07180": {
846
852
  "type": "float",
847
853
  "desc": "Nonrefundable credit for child and dependent care expenses from Form 2441",
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-locals
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 = '{}-{}'.format(fname[:-4], str(tax_year)[2:])
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'.format(msg)
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'.format(msg)
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'.format(msg)
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'.format(msg)
112
+ self.errmsg += f'ERROR: {msg}\n'
113
113
  elif isinstance(input_data, pd.DataFrame):
114
- inp = 'df-{}'.format(str(tax_year)[2:])
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'.format(msg)
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 = '-{}'.format(fname[:-5])
127
+ bas = f'-{fname[:-5]}'
128
128
  else:
129
129
  msg = 'BASELINE file name does not end in .json'
130
- self.errmsg += 'ERROR: {}\n'.format(msg)
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'.format(msg)
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'.format(msg)
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'.format(fname)
154
- self.errmsg += 'ERROR: REFORM file name {}\n'.format(msg)
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'.format(rfm)
158
- self.errmsg += 'ERROR: REFORM file {}\n'.format(msg)
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 += '{}'.format(refname[:-5])
168
+ ref += f'{refname[:-5]}'
169
169
  else:
170
170
  msg = 'TaxCalcIO.ctor: reform is neither None nor str'
171
- self.errmsg += 'ERROR: {}\n'.format(msg)
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 = '-{}'.format(fname[:-5])
181
+ asm = f'-{fname[:-5]}'
182
182
  else:
183
183
  msg = 'ASSUMP file name does not end in .json'
184
- self.errmsg += 'ERROR: {}\n'.format(msg)
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'.format(msg)
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'.format(msg)
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'.format(msg)
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'.format(msg)
206
+ self.errmsg += f'ERROR: {msg}\n'
207
207
  # create OUTPUT file name and delete any existing output files
208
- output_filename = '{}{}{}{}.csv'.format(inp, bas, ref, asm)
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-locals
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.__str__()
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.__str__()
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.__str__()
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.__str__()
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.__str__()
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
- msg = msg.format(var)
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-branches,too-many-locals
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.format(tkind))
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}'.format(decile)
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 = ('<html>\n'
780
- '<head><title>{}</title></head>\n'
781
- '<body><center<h1>{}</h1></center></body>\n'
782
- '</html>\n').format(title, reason)
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