pyproforma 0.2.2__tar.gz → 0.2.4__tar.gz
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.
- {pyproforma-0.2.2/pyproforma.egg-info → pyproforma-0.2.4}/PKG-INFO +12 -10
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/__init__.py +26 -21
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/chart/renderers/matplotlib_renderer.py +2 -2
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/charts/chart_def.py +1 -2
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/charts/charts.py +4 -1
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/compare/model_comparison.py +3 -1
- pyproforma-0.2.4/pyproforma/engine/__init__.py +14 -0
- {pyproforma-0.2.2/pyproforma → pyproforma-0.2.4/pyproforma/engine}/calculation_engine.py +23 -15
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/engine}/line_item_values.py +44 -2
- {pyproforma-0.2.2/pyproforma → pyproforma-0.2.4/pyproforma/engine}/model_namespace.py +22 -2
- pyproforma-0.2.4/pyproforma/explorer/__init__.py +4 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/explorer/app.py +117 -45
- pyproforma-0.2.4/pyproforma/explorer/components.py +128 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/base.html +138 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/chart_view.html +28 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/index.html +80 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/inputs.html +107 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/line_item.html +173 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/table_view.html +18 -0
- pyproforma-0.2.4/pyproforma/explorer/templates/view.html +178 -0
- pyproforma-0.2.4/pyproforma/proforma_model.py +248 -0
- pyproforma-0.2.4/pyproforma/results/__init__.py +13 -0
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/results}/line_item_result.py +26 -82
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/results}/line_item_selection.py +1 -1
- pyproforma-0.2.4/pyproforma/results/line_item_stat.py +120 -0
- pyproforma-0.2.4/pyproforma/results/scalar_result.py +41 -0
- pyproforma-0.2.4/pyproforma/results/tags_namespace.py +34 -0
- pyproforma-0.2.4/pyproforma/specs/__init__.py +33 -0
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/specs}/debt_line.py +59 -52
- pyproforma-0.2.4/pyproforma/specs/fixed_line.py +35 -0
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/specs}/formula_line.py +1 -1
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/specs}/input_line.py +47 -12
- {pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/specs}/line_item.py +25 -9
- pyproforma-0.2.4/pyproforma/specs/scalar_input_line.py +54 -0
- pyproforma-0.2.4/pyproforma/specs/scalar_line.py +38 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/colors.py +25 -26
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/excel.py +14 -7
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/format_value.py +6 -2
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/table_class.py +15 -5
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/tables/row_types.py +15 -8
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/tables/table_def.py +1 -2
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/tables/tables.py +5 -2
- {pyproforma-0.2.2 → pyproforma-0.2.4/pyproforma.egg-info}/PKG-INFO +12 -10
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma.egg-info/SOURCES.txt +25 -12
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma.egg-info/requires.txt +11 -6
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproject.toml +26 -14
- pyproforma-0.2.2/pyproforma/explorer/__init__.py +0 -3
- pyproforma-0.2.2/pyproforma/explorer/components.py +0 -42
- pyproforma-0.2.2/pyproforma/line_items/__init__.py +0 -38
- pyproforma-0.2.2/pyproforma/line_items/fixed_line.py +0 -72
- pyproforma-0.2.2/pyproforma/proforma_model.py +0 -191
- pyproforma-0.2.2/pyproforma/tags_namespace.py +0 -220
- {pyproforma-0.2.2 → pyproforma-0.2.4}/LICENSE +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/MANIFEST.in +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/README.md +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/chart/__init__.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/chart/chart.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/chart/renderers/__init__.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/chart/renderers/base.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/charts/__init__.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/compare/__init__.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/reserved_words.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/__init__.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/bootstrap_html_renderer.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/table/html_renderer.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma/tables/__init__.py +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma.egg-info/dependency_links.txt +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/pyproforma.egg-info/top_level.txt +0 -0
- {pyproforma-0.2.2 → pyproforma-0.2.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyproforma
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A Python package for financial modeling and reporting
|
|
5
5
|
Author-email: Robert Hannay <rhannay@gmail.com>
|
|
6
6
|
Maintainer-email: Robert Hannay <rhannay@gmail.com>
|
|
@@ -22,19 +22,21 @@ Classifier: Operating System :: OS Independent
|
|
|
22
22
|
Requires-Python: >=3.9
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Provides-Extra: dev
|
|
32
|
-
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
33
|
-
Requires-Dist: pytest-cov; extra == "dev"
|
|
25
|
+
Provides-Extra: pandas
|
|
26
|
+
Requires-Dist: pandas>=1.3.0; extra == "pandas"
|
|
27
|
+
Provides-Extra: excel
|
|
28
|
+
Requires-Dist: openpyxl>=3.0.0; extra == "excel"
|
|
29
|
+
Provides-Extra: charts
|
|
30
|
+
Requires-Dist: matplotlib>=3.5.0; extra == "charts"
|
|
34
31
|
Provides-Extra: notebook
|
|
35
32
|
Requires-Dist: ipython>=7.0.0; extra == "notebook"
|
|
36
33
|
Provides-Extra: explorer
|
|
37
34
|
Requires-Dist: flask>=2.0.0; extra == "explorer"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
38
|
+
Requires-Dist: pandas>=1.3.0; extra == "dev"
|
|
39
|
+
Requires-Dist: openpyxl>=3.0.0; extra == "dev"
|
|
38
40
|
Dynamic: license-file
|
|
39
41
|
|
|
40
42
|
# pyproforma
|
|
@@ -3,7 +3,26 @@ PyProforma - A lightweight financial modeling framework.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from .charts.chart_def import ChartDef
|
|
6
|
-
from .
|
|
6
|
+
from .compare import ModelComparison
|
|
7
|
+
from .engine import LineItemValue, LineItemValues
|
|
8
|
+
from .proforma_model import ProformaModel
|
|
9
|
+
from .results import LineItemResult, LineItemSelection, ScalarResult
|
|
10
|
+
from .results.tags_namespace import TagNamespace
|
|
11
|
+
from .specs import (
|
|
12
|
+
DebtCalculator,
|
|
13
|
+
DebtConfig,
|
|
14
|
+
DebtInterestLine,
|
|
15
|
+
DebtPrincipalLine,
|
|
16
|
+
FixedLine,
|
|
17
|
+
FormulaLine,
|
|
18
|
+
InputLine,
|
|
19
|
+
LineItem,
|
|
20
|
+
ScalarInputLine,
|
|
21
|
+
ScalarLine,
|
|
22
|
+
create_debt_lines,
|
|
23
|
+
)
|
|
24
|
+
from .table import Format, NumberFormatSpec
|
|
25
|
+
from .tables import Tables
|
|
7
26
|
from .tables.row_types import (
|
|
8
27
|
BlankRow,
|
|
9
28
|
CumulativeChangeRow,
|
|
@@ -16,25 +35,7 @@ from .tables.row_types import (
|
|
|
16
35
|
TagItemsRow,
|
|
17
36
|
TagTotalRow,
|
|
18
37
|
)
|
|
19
|
-
from .
|
|
20
|
-
DebtCalculator,
|
|
21
|
-
DebtInterestLine,
|
|
22
|
-
DebtPrincipalLine,
|
|
23
|
-
FixedLine,
|
|
24
|
-
FormulaLine,
|
|
25
|
-
InputLine,
|
|
26
|
-
LineItem,
|
|
27
|
-
LineItemResult,
|
|
28
|
-
LineItemSelection,
|
|
29
|
-
LineItemValue,
|
|
30
|
-
LineItemValues,
|
|
31
|
-
create_debt_lines,
|
|
32
|
-
)
|
|
33
|
-
from .table import Format, NumberFormatSpec
|
|
34
|
-
from .compare import ModelComparison
|
|
35
|
-
from .proforma_model import ProformaModel
|
|
36
|
-
from .tables import Tables
|
|
37
|
-
from .tags_namespace import ModelTagNamespace
|
|
38
|
+
from .tables.table_def import TableDef
|
|
38
39
|
|
|
39
40
|
__all__ = [
|
|
40
41
|
"ProformaModel",
|
|
@@ -53,16 +54,20 @@ __all__ = [
|
|
|
53
54
|
"FixedLine",
|
|
54
55
|
"FormulaLine",
|
|
55
56
|
"InputLine",
|
|
57
|
+
"ScalarLine",
|
|
58
|
+
"ScalarInputLine",
|
|
59
|
+
"ScalarResult",
|
|
56
60
|
"DebtPrincipalLine",
|
|
57
61
|
"DebtInterestLine",
|
|
58
62
|
"DebtCalculator",
|
|
63
|
+
"DebtConfig",
|
|
59
64
|
"create_debt_lines",
|
|
60
65
|
"LineItem",
|
|
61
66
|
"LineItemValues",
|
|
62
67
|
"LineItemValue",
|
|
63
68
|
"LineItemResult",
|
|
64
69
|
"LineItemSelection",
|
|
65
|
-
"
|
|
70
|
+
"TagNamespace",
|
|
66
71
|
"Tables",
|
|
67
72
|
"ModelComparison",
|
|
68
73
|
"Format",
|
|
@@ -7,8 +7,8 @@ chart types. Applies NumberFormatSpec to y-axis tick labels when set.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
from pyproforma.chart.renderers.base import ChartRenderer
|
|
11
10
|
from pyproforma.chart.chart import Chart as ChartSpec
|
|
11
|
+
from pyproforma.chart.renderers.base import ChartRenderer
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class MatplotlibRenderer(ChartRenderer):
|
|
@@ -26,7 +26,6 @@ class MatplotlibRenderer(ChartRenderer):
|
|
|
26
26
|
matplotlib.figure.Figure
|
|
27
27
|
"""
|
|
28
28
|
import matplotlib.pyplot as plt
|
|
29
|
-
import numpy as np
|
|
30
29
|
|
|
31
30
|
fig, ax = plt.subplots(figsize=figsize)
|
|
32
31
|
|
|
@@ -109,6 +108,7 @@ class MatplotlibRenderer(ChartRenderer):
|
|
|
109
108
|
return
|
|
110
109
|
|
|
111
110
|
from matplotlib.ticker import FuncFormatter
|
|
111
|
+
|
|
112
112
|
from pyproforma.table.format_value import format_value
|
|
113
113
|
|
|
114
114
|
fmt = spec.value_format
|
|
@@ -6,8 +6,7 @@ chart type, title, and optional per-series colors. Accepts either the dataclass
|
|
|
6
6
|
form (IDE autocomplete, validation) or a plain dict (JSON-serializable configs).
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from dataclasses import dataclass
|
|
10
|
-
from typing import Union
|
|
9
|
+
from dataclasses import dataclass
|
|
11
10
|
|
|
12
11
|
from pyproforma.chart.chart import ChartType
|
|
13
12
|
|
|
@@ -15,6 +15,7 @@ from typing import TYPE_CHECKING
|
|
|
15
15
|
from pyproforma.chart.chart import Chart, ChartSeries, ChartType
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
|
+
from pyproforma.charts.chart_def import ChartDef
|
|
18
19
|
from pyproforma.proforma_model import ProformaModel
|
|
19
20
|
|
|
20
21
|
|
|
@@ -113,7 +114,9 @@ class Charts:
|
|
|
113
114
|
Examples:
|
|
114
115
|
>>> model.charts.line_items(["revenue", "expenses"]).show()
|
|
115
116
|
>>> model.charts.line_items(["revenue", "cogs"], chart_type="stacked_bar").show()
|
|
116
|
-
>>> model.charts.line_items(
|
|
117
|
+
>>> model.charts.line_items( # noqa: E501
|
|
118
|
+
... ["revenue", "expenses"], value_format=Format.MILLIONS_M
|
|
119
|
+
... ).show()
|
|
117
120
|
"""
|
|
118
121
|
for name in names:
|
|
119
122
|
self._validate_line_item(name)
|
|
@@ -293,7 +293,9 @@ class ModelComparison:
|
|
|
293
293
|
for model, label in zip(self.models, self.labels):
|
|
294
294
|
row = [Cell(value=label, align="left")]
|
|
295
295
|
for period in self.common_periods:
|
|
296
|
-
row.append(
|
|
296
|
+
row.append(
|
|
297
|
+
Cell(value=model.get_value(item_name, period), value_format=value_format)
|
|
298
|
+
)
|
|
297
299
|
all_rows.append(row)
|
|
298
300
|
|
|
299
301
|
# Difference row(s)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Calculation internals — engine and namespace.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .calculation_engine import calculate_line_items
|
|
6
|
+
from .line_item_values import LineItemValue, LineItemValues
|
|
7
|
+
from .model_namespace import ModelNamespace
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"calculate_line_items",
|
|
11
|
+
"LineItemValues",
|
|
12
|
+
"LineItemValue",
|
|
13
|
+
"ModelNamespace",
|
|
14
|
+
]
|
|
@@ -8,7 +8,7 @@ values across periods.
|
|
|
8
8
|
from typing import TYPE_CHECKING, Any
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
|
-
from .
|
|
11
|
+
from .line_item_values import LineItemValues
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def calculate_line_items(
|
|
@@ -27,13 +27,15 @@ def calculate_line_items(
|
|
|
27
27
|
Returns:
|
|
28
28
|
LineItemValues: Populated container with all calculated values.
|
|
29
29
|
"""
|
|
30
|
-
from .
|
|
31
|
-
from .
|
|
32
|
-
from .
|
|
33
|
-
from .
|
|
34
|
-
|
|
30
|
+
from pyproforma.specs.debt_line import DebtBase
|
|
31
|
+
from pyproforma.specs.fixed_line import FixedLine
|
|
32
|
+
from pyproforma.specs.formula_line import FormulaLine
|
|
33
|
+
from pyproforma.specs.input_line import InputLine
|
|
34
|
+
|
|
35
|
+
from .line_item_values import LineItemValues
|
|
35
36
|
from .model_namespace import ModelNamespace
|
|
36
37
|
|
|
38
|
+
# scalar_names are already resolved into the scalars dict — skip them here
|
|
37
39
|
li = LineItemValues(periods=periods, names=model.line_item_names, model=model)
|
|
38
40
|
|
|
39
41
|
fixed_items = []
|
|
@@ -80,7 +82,7 @@ def calculate_line_items(
|
|
|
80
82
|
still_pending.append(name)
|
|
81
83
|
else:
|
|
82
84
|
raise ValueError(
|
|
83
|
-
f"Error evaluating formula for '{line_item.name}' in period {period}: {e}"
|
|
85
|
+
f"Error evaluating formula for '{line_item.name}' in period {period}: {e}" # noqa: E501
|
|
84
86
|
) from e
|
|
85
87
|
|
|
86
88
|
if len(still_pending) == len(remaining):
|
|
@@ -100,20 +102,19 @@ def _calculate_single_line_item(
|
|
|
100
102
|
period: int,
|
|
101
103
|
model: Any = None,
|
|
102
104
|
) -> Any:
|
|
103
|
-
from .
|
|
104
|
-
from .
|
|
105
|
-
from .
|
|
106
|
-
from .
|
|
105
|
+
from pyproforma.specs.debt_line import DebtBase
|
|
106
|
+
from pyproforma.specs.fixed_line import FixedLine
|
|
107
|
+
from pyproforma.specs.formula_line import FormulaLine
|
|
108
|
+
from pyproforma.specs.input_line import InputLine
|
|
107
109
|
|
|
108
110
|
if isinstance(line_item, InputLine):
|
|
109
111
|
input_values = getattr(model, "_input_line_values", {})
|
|
110
112
|
period_values = input_values.get(line_item.name, {})
|
|
111
|
-
|
|
112
|
-
if value is None:
|
|
113
|
+
if period not in period_values:
|
|
113
114
|
raise ValueError(
|
|
114
115
|
f"No input value for '{line_item.name}' in period {period}"
|
|
115
116
|
)
|
|
116
|
-
return value
|
|
117
|
+
return period_values[period] # None is a valid value — means "no input this period"
|
|
117
118
|
|
|
118
119
|
if isinstance(line_item, FixedLine):
|
|
119
120
|
value = line_item.get_value(period)
|
|
@@ -141,8 +142,15 @@ def _calculate_single_line_item(
|
|
|
141
142
|
return value
|
|
142
143
|
|
|
143
144
|
if isinstance(line_item, DebtBase):
|
|
145
|
+
debt_calculators = getattr(model, "_debt_calculators", {})
|
|
146
|
+
calculator = debt_calculators.get(id(line_item.config))
|
|
147
|
+
if calculator is None:
|
|
148
|
+
raise ValueError(
|
|
149
|
+
f"No calculator found for debt line '{line_item.name}'. "
|
|
150
|
+
f"Ensure the model was instantiated via ProformaModel.__init__."
|
|
151
|
+
)
|
|
144
152
|
try:
|
|
145
|
-
value = line_item.eval(ns, period)
|
|
153
|
+
value = line_item.eval(ns, period, calculator)
|
|
146
154
|
except (AttributeError, KeyError):
|
|
147
155
|
raise
|
|
148
156
|
except Exception as e:
|
{pyproforma-0.2.2/pyproforma/line_items → pyproforma-0.2.4/pyproforma/engine}/line_item_values.py
RENAMED
|
@@ -9,7 +9,49 @@ from typing import TYPE_CHECKING, Any
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from pyproforma.proforma_model import ProformaModel
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
class TagSum:
|
|
14
|
+
"""Sums all line items sharing a tag for a given period. Used inside formula evaluation."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, model: "ProformaModel", li: "LineItemValues", tag: str):
|
|
17
|
+
self._model = model
|
|
18
|
+
self._li = li
|
|
19
|
+
self._tag = tag
|
|
20
|
+
|
|
21
|
+
def __getitem__(self, period: int) -> float:
|
|
22
|
+
if period not in self._model.periods:
|
|
23
|
+
raise KeyError(
|
|
24
|
+
f"Period {period} not found in model. "
|
|
25
|
+
f"Available periods: {self._model.periods}"
|
|
26
|
+
)
|
|
27
|
+
total = 0.0
|
|
28
|
+
for name in self._model.line_item_names:
|
|
29
|
+
spec = getattr(self._model.__class__, name)
|
|
30
|
+
if hasattr(spec, "tags") and self._tag in spec.tags:
|
|
31
|
+
try:
|
|
32
|
+
value = self._li.get(name, period)
|
|
33
|
+
if value is not None:
|
|
34
|
+
total += value
|
|
35
|
+
except (AttributeError, KeyError):
|
|
36
|
+
pass
|
|
37
|
+
return total
|
|
38
|
+
|
|
39
|
+
def __repr__(self):
|
|
40
|
+
return f"TagSum(tag={self._tag!r})"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TagNamespace:
|
|
44
|
+
"""Namespace for tag-based summation inside formula evaluation (li.tag["revenue"][t])."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, model: "ProformaModel", li: "LineItemValues"):
|
|
47
|
+
self._model = model
|
|
48
|
+
self._li = li
|
|
49
|
+
|
|
50
|
+
def __getitem__(self, tag: str) -> TagSum:
|
|
51
|
+
return TagSum(self._model, self._li, tag)
|
|
52
|
+
|
|
53
|
+
def __repr__(self):
|
|
54
|
+
return f"TagNamespace(model={self._model.__class__.__name__})"
|
|
13
55
|
|
|
14
56
|
|
|
15
57
|
class LineItemValues:
|
|
@@ -77,7 +119,7 @@ class LineItemValues:
|
|
|
77
119
|
self._tag_namespace = TagNamespace(model, self) if model else None
|
|
78
120
|
|
|
79
121
|
@property
|
|
80
|
-
def tag(self) -> TagNamespace:
|
|
122
|
+
def tag(self) -> "TagNamespace":
|
|
81
123
|
"""
|
|
82
124
|
Access line items by tags.
|
|
83
125
|
|
|
@@ -12,7 +12,24 @@ Scalar line items (FixedLine with value=, or scalar InputLine) are accessed dire
|
|
|
12
12
|
from typing import TYPE_CHECKING
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
-
from pyproforma.
|
|
15
|
+
from pyproforma.engine.line_item_values import LineItemValues
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _ScalarValue(float):
|
|
19
|
+
"""float subclass returned for scalar line items in formulas.
|
|
20
|
+
|
|
21
|
+
Raises a clear TypeError if the caller tries li.rate[t] instead of li.rate.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __new__(cls, value: float, name: str):
|
|
25
|
+
inst = super().__new__(cls, value)
|
|
26
|
+
inst._name = name
|
|
27
|
+
return inst
|
|
28
|
+
|
|
29
|
+
def __getitem__(self, key):
|
|
30
|
+
raise TypeError(
|
|
31
|
+
f"'{self._name}' is a scalar — use li.{self._name}, not li.{self._name}[{key}]"
|
|
32
|
+
)
|
|
16
33
|
|
|
17
34
|
|
|
18
35
|
class ModelNamespace:
|
|
@@ -48,7 +65,10 @@ class ModelNamespace:
|
|
|
48
65
|
return li.tag
|
|
49
66
|
|
|
50
67
|
if name in scalars:
|
|
51
|
-
|
|
68
|
+
value = scalars[name]
|
|
69
|
+
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
|
70
|
+
return _ScalarValue(value, name)
|
|
71
|
+
return value
|
|
52
72
|
|
|
53
73
|
li_error = None
|
|
54
74
|
try:
|