rustfava 0.1.0__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 (187) hide show
  1. rustfava/__init__.py +30 -0
  2. rustfava/_ctx_globals_class.py +55 -0
  3. rustfava/api_models.py +36 -0
  4. rustfava/application.py +534 -0
  5. rustfava/beans/__init__.py +6 -0
  6. rustfava/beans/abc.py +327 -0
  7. rustfava/beans/account.py +79 -0
  8. rustfava/beans/create.py +377 -0
  9. rustfava/beans/flags.py +20 -0
  10. rustfava/beans/funcs.py +38 -0
  11. rustfava/beans/helpers.py +52 -0
  12. rustfava/beans/ingest.py +75 -0
  13. rustfava/beans/load.py +31 -0
  14. rustfava/beans/prices.py +151 -0
  15. rustfava/beans/protocols.py +82 -0
  16. rustfava/beans/str.py +454 -0
  17. rustfava/beans/types.py +63 -0
  18. rustfava/cli.py +187 -0
  19. rustfava/context.py +13 -0
  20. rustfava/core/__init__.py +729 -0
  21. rustfava/core/accounts.py +161 -0
  22. rustfava/core/attributes.py +145 -0
  23. rustfava/core/budgets.py +207 -0
  24. rustfava/core/charts.py +301 -0
  25. rustfava/core/commodities.py +37 -0
  26. rustfava/core/conversion.py +229 -0
  27. rustfava/core/documents.py +87 -0
  28. rustfava/core/extensions.py +132 -0
  29. rustfava/core/fava_options.py +255 -0
  30. rustfava/core/file.py +542 -0
  31. rustfava/core/filters.py +484 -0
  32. rustfava/core/group_entries.py +97 -0
  33. rustfava/core/ingest.py +509 -0
  34. rustfava/core/inventory.py +167 -0
  35. rustfava/core/misc.py +105 -0
  36. rustfava/core/module_base.py +18 -0
  37. rustfava/core/number.py +106 -0
  38. rustfava/core/query.py +180 -0
  39. rustfava/core/query_shell.py +301 -0
  40. rustfava/core/tree.py +265 -0
  41. rustfava/core/watcher.py +219 -0
  42. rustfava/ext/__init__.py +232 -0
  43. rustfava/ext/auto_commit.py +61 -0
  44. rustfava/ext/portfolio_list/PortfolioList.js +34 -0
  45. rustfava/ext/portfolio_list/__init__.py +29 -0
  46. rustfava/ext/portfolio_list/templates/PortfolioList.html +15 -0
  47. rustfava/ext/rustfava_ext_test/RustfavaExtTest.js +42 -0
  48. rustfava/ext/rustfava_ext_test/__init__.py +207 -0
  49. rustfava/ext/rustfava_ext_test/templates/RustfavaExtTest.html +45 -0
  50. rustfava/ext/rustfava_ext_test/templates/RustfavaExtTestInclude.html +1 -0
  51. rustfava/help/__init__.py +15 -0
  52. rustfava/help/_index.md +29 -0
  53. rustfava/help/beancount_syntax.md +156 -0
  54. rustfava/help/budgets.md +31 -0
  55. rustfava/help/conversion.md +29 -0
  56. rustfava/help/extensions.md +111 -0
  57. rustfava/help/features.md +179 -0
  58. rustfava/help/filters.md +103 -0
  59. rustfava/help/import.md +27 -0
  60. rustfava/help/options.md +289 -0
  61. rustfava/helpers.py +30 -0
  62. rustfava/internal_api.py +221 -0
  63. rustfava/json_api.py +952 -0
  64. rustfava/plugins/__init__.py +3 -0
  65. rustfava/plugins/link_documents.py +107 -0
  66. rustfava/plugins/tag_discovered_documents.py +44 -0
  67. rustfava/py.typed +0 -0
  68. rustfava/rustledger/__init__.py +31 -0
  69. rustfava/rustledger/constants.py +76 -0
  70. rustfava/rustledger/engine.py +485 -0
  71. rustfava/rustledger/loader.py +273 -0
  72. rustfava/rustledger/options.py +202 -0
  73. rustfava/rustledger/query.py +331 -0
  74. rustfava/rustledger/types.py +830 -0
  75. rustfava/serialisation.py +220 -0
  76. rustfava/static/app.css +2988 -0
  77. rustfava/static/app.css.map +7 -0
  78. rustfava/static/app.js +12854 -0
  79. rustfava/static/app.js.map +7 -0
  80. rustfava/static/beancount-JFV44ZVZ.css +5 -0
  81. rustfava/static/beancount-JFV44ZVZ.css.map +7 -0
  82. rustfava/static/beancount-VTTKRGSK.js +4642 -0
  83. rustfava/static/beancount-VTTKRGSK.js.map +7 -0
  84. rustfava/static/bql-MGFRUMBP.js +333 -0
  85. rustfava/static/bql-MGFRUMBP.js.map +7 -0
  86. rustfava/static/chunk-E7ZF4ASL.js +23061 -0
  87. rustfava/static/chunk-E7ZF4ASL.js.map +7 -0
  88. rustfava/static/chunk-V24TLQHT.js +12673 -0
  89. rustfava/static/chunk-V24TLQHT.js.map +7 -0
  90. rustfava/static/favicon.ico +0 -0
  91. rustfava/static/fira-mono-cyrillic-400-normal-BLAGXRCE.woff2 +0 -0
  92. rustfava/static/fira-mono-cyrillic-500-normal-EN7JUAAW.woff2 +0 -0
  93. rustfava/static/fira-mono-cyrillic-ext-400-normal-EX7VARTS.woff2 +0 -0
  94. rustfava/static/fira-mono-cyrillic-ext-500-normal-ZDPTUPRR.woff2 +0 -0
  95. rustfava/static/fira-mono-greek-400-normal-COGHKMOA.woff2 +0 -0
  96. rustfava/static/fira-mono-greek-500-normal-4EN2PKZT.woff2 +0 -0
  97. rustfava/static/fira-mono-greek-ext-400-normal-DYEQIJH7.woff2 +0 -0
  98. rustfava/static/fira-mono-greek-ext-500-normal-SG73CVKQ.woff2 +0 -0
  99. rustfava/static/fira-mono-latin-400-normal-NA3VLV7E.woff2 +0 -0
  100. rustfava/static/fira-mono-latin-500-normal-YC77GFWD.woff2 +0 -0
  101. rustfava/static/fira-mono-latin-ext-400-normal-DIKTZ5PW.woff2 +0 -0
  102. rustfava/static/fira-mono-latin-ext-500-normal-ZWY4UO4V.woff2 +0 -0
  103. rustfava/static/fira-mono-symbols2-400-normal-UITXT77Q.woff2 +0 -0
  104. rustfava/static/fira-mono-symbols2-500-normal-VWPC2EFN.woff2 +0 -0
  105. rustfava/static/fira-sans-cyrillic-400-normal-KLQMBCA6.woff2 +0 -0
  106. rustfava/static/fira-sans-cyrillic-500-normal-NFG7UD6J.woff2 +0 -0
  107. rustfava/static/fira-sans-cyrillic-ext-400-normal-GWO44OPC.woff2 +0 -0
  108. rustfava/static/fira-sans-cyrillic-ext-500-normal-SP47E5SC.woff2 +0 -0
  109. rustfava/static/fira-sans-greek-400-normal-UMQBTLC3.woff2 +0 -0
  110. rustfava/static/fira-sans-greek-500-normal-4ZKHN4FQ.woff2 +0 -0
  111. rustfava/static/fira-sans-greek-ext-400-normal-O2DVJAJZ.woff2 +0 -0
  112. rustfava/static/fira-sans-greek-ext-500-normal-SK6GNWGO.woff2 +0 -0
  113. rustfava/static/fira-sans-latin-400-normal-OYYTPMAV.woff2 +0 -0
  114. rustfava/static/fira-sans-latin-500-normal-SMQPZW5A.woff2 +0 -0
  115. rustfava/static/fira-sans-latin-ext-400-normal-OAUP3WK5.woff2 +0 -0
  116. rustfava/static/fira-sans-latin-ext-500-normal-LY3YDR5Y.woff2 +0 -0
  117. rustfava/static/fira-sans-vietnamese-400-normal-OBMQ72MR.woff2 +0 -0
  118. rustfava/static/fira-sans-vietnamese-500-normal-Y4NZR5EU.woff2 +0 -0
  119. rustfava/static/source-code-pro-cyrillic-400-normal-TO22V6M3.woff2 +0 -0
  120. rustfava/static/source-code-pro-cyrillic-500-normal-OGBWWWYW.woff2 +0 -0
  121. rustfava/static/source-code-pro-cyrillic-ext-400-normal-XH44UCIA.woff2 +0 -0
  122. rustfava/static/source-code-pro-cyrillic-ext-500-normal-3Z6MMVM6.woff2 +0 -0
  123. rustfava/static/source-code-pro-greek-400-normal-OUXXUQWK.woff2 +0 -0
  124. rustfava/static/source-code-pro-greek-500-normal-JA2Z5UXO.woff2 +0 -0
  125. rustfava/static/source-code-pro-greek-ext-400-normal-WCDKMX7U.woff2 +0 -0
  126. rustfava/static/source-code-pro-greek-ext-500-normal-ZHVI4VKW.woff2 +0 -0
  127. rustfava/static/source-code-pro-latin-400-normal-QOGTXED5.woff2 +0 -0
  128. rustfava/static/source-code-pro-latin-500-normal-X57QEOLQ.woff2 +0 -0
  129. rustfava/static/source-code-pro-latin-ext-400-normal-QXC74NBF.woff2 +0 -0
  130. rustfava/static/source-code-pro-latin-ext-500-normal-QGOY7MTT.woff2 +0 -0
  131. rustfava/static/source-code-pro-vietnamese-400-normal-NPDCDTBA.woff2 +0 -0
  132. rustfava/static/source-code-pro-vietnamese-500-normal-M6PJKTR5.woff2 +0 -0
  133. rustfava/static/tree-sitter-beancount-MLXFQBZ5.wasm +0 -0
  134. rustfava/static/web-tree-sitter-RNOQ6E74.wasm +0 -0
  135. rustfava/template_filters.py +64 -0
  136. rustfava/templates/_journal_table.html +156 -0
  137. rustfava/templates/_layout.html +26 -0
  138. rustfava/templates/_query_table.html +88 -0
  139. rustfava/templates/beancount_file +18 -0
  140. rustfava/templates/help.html +23 -0
  141. rustfava/templates/macros/_account_macros.html +5 -0
  142. rustfava/templates/macros/_commodity_macros.html +13 -0
  143. rustfava/translations/bg/LC_MESSAGES/messages.mo +0 -0
  144. rustfava/translations/bg/LC_MESSAGES/messages.po +618 -0
  145. rustfava/translations/ca/LC_MESSAGES/messages.mo +0 -0
  146. rustfava/translations/ca/LC_MESSAGES/messages.po +618 -0
  147. rustfava/translations/de/LC_MESSAGES/messages.mo +0 -0
  148. rustfava/translations/de/LC_MESSAGES/messages.po +618 -0
  149. rustfava/translations/es/LC_MESSAGES/messages.mo +0 -0
  150. rustfava/translations/es/LC_MESSAGES/messages.po +619 -0
  151. rustfava/translations/fa/LC_MESSAGES/messages.mo +0 -0
  152. rustfava/translations/fa/LC_MESSAGES/messages.po +618 -0
  153. rustfava/translations/fr/LC_MESSAGES/messages.mo +0 -0
  154. rustfava/translations/fr/LC_MESSAGES/messages.po +618 -0
  155. rustfava/translations/ja/LC_MESSAGES/messages.mo +0 -0
  156. rustfava/translations/ja/LC_MESSAGES/messages.po +618 -0
  157. rustfava/translations/nl/LC_MESSAGES/messages.mo +0 -0
  158. rustfava/translations/nl/LC_MESSAGES/messages.po +617 -0
  159. rustfava/translations/pt/LC_MESSAGES/messages.mo +0 -0
  160. rustfava/translations/pt/LC_MESSAGES/messages.po +617 -0
  161. rustfava/translations/pt_BR/LC_MESSAGES/messages.mo +0 -0
  162. rustfava/translations/pt_BR/LC_MESSAGES/messages.po +618 -0
  163. rustfava/translations/ru/LC_MESSAGES/messages.mo +0 -0
  164. rustfava/translations/ru/LC_MESSAGES/messages.po +617 -0
  165. rustfava/translations/sk/LC_MESSAGES/messages.mo +0 -0
  166. rustfava/translations/sk/LC_MESSAGES/messages.po +623 -0
  167. rustfava/translations/sv/LC_MESSAGES/messages.mo +0 -0
  168. rustfava/translations/sv/LC_MESSAGES/messages.po +618 -0
  169. rustfava/translations/uk/LC_MESSAGES/messages.mo +0 -0
  170. rustfava/translations/uk/LC_MESSAGES/messages.po +618 -0
  171. rustfava/translations/zh/LC_MESSAGES/messages.mo +0 -0
  172. rustfava/translations/zh/LC_MESSAGES/messages.po +618 -0
  173. rustfava/translations/zh_Hant_TW/LC_MESSAGES/messages.mo +0 -0
  174. rustfava/translations/zh_Hant_TW/LC_MESSAGES/messages.po +618 -0
  175. rustfava/util/__init__.py +157 -0
  176. rustfava/util/date.py +576 -0
  177. rustfava/util/excel.py +118 -0
  178. rustfava/util/ranking.py +79 -0
  179. rustfava/util/sets.py +18 -0
  180. rustfava/util/unreachable.py +20 -0
  181. rustfava-0.1.0.dist-info/METADATA +102 -0
  182. rustfava-0.1.0.dist-info/RECORD +187 -0
  183. rustfava-0.1.0.dist-info/WHEEL +5 -0
  184. rustfava-0.1.0.dist-info/entry_points.txt +2 -0
  185. rustfava-0.1.0.dist-info/licenses/AUTHORS +11 -0
  186. rustfava-0.1.0.dist-info/licenses/LICENSE +21 -0
  187. rustfava-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,63 @@
1
+ """Typing helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from decimal import Decimal
7
+ from typing import Any
8
+ from typing import TypedDict
9
+
10
+ from rustfava.beans.abc import Directive
11
+ from rustfava.helpers import BeancountError
12
+
13
+
14
+ class BeancountOptions(TypedDict):
15
+ """Beancount options."""
16
+
17
+ title: str
18
+ filename: str
19
+
20
+ name_assets: str
21
+ name_liabilities: str
22
+ name_equity: str
23
+ name_income: str
24
+ name_expenses: str
25
+
26
+ account_current_conversions: str
27
+ account_current_earnings: str
28
+ account_previous_balances: str
29
+ account_previous_conversions: str
30
+ account_previous_earnings: str
31
+ account_rounding: str | None
32
+ account_unrealized_gains: str
33
+
34
+ booking_method: Any # beancount.core.data.Booking enum
35
+ commodities: set[str]
36
+ conversion_currency: str
37
+ dcontext: Any # beancount.core.display_context.DisplayContext
38
+ display_precision: dict[str, Decimal]
39
+ documents: Sequence[str]
40
+ include: Sequence[str]
41
+ infer_tolerance_from_cost: bool
42
+ inferred_tolerance_default: dict[str, Decimal]
43
+ inferred_tolerance_multiplier: Decimal
44
+ input_hash: str
45
+ insert_pythonpath: bool
46
+ operating_currency: Sequence[str]
47
+ plugin: Sequence[tuple[str, str | None]]
48
+ plugin_processing_mode: str
49
+ pythonpath: Sequence[str]
50
+ render_commas: bool
51
+ tolerance_multiplier: Decimal
52
+
53
+ # deprecated ones
54
+ allow_deprecated_none_for_tags_and_links: bool
55
+ allow_pipe_separator: bool
56
+ long_string_maxlines: int
57
+
58
+
59
+ LoaderResult = tuple[
60
+ Sequence[Directive],
61
+ Sequence[BeancountError],
62
+ BeancountOptions,
63
+ ]
rustfava/cli.py ADDED
@@ -0,0 +1,187 @@
1
+ """The command-line interface for rustfava."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import errno
6
+ import os
7
+ from pathlib import Path
8
+
9
+ import click
10
+
11
+
12
+ class AddressInUse(click.ClickException): # noqa: D101
13
+ def __init__(self, port: int) -> None: # pragma: no cover
14
+ super().__init__(
15
+ f"Cannot start rustfava because port {port} is already in use."
16
+ "\nPlease choose a different port with the '-p' option.",
17
+ )
18
+
19
+
20
+ class NonAbsolutePathError(click.UsageError): # noqa: D101
21
+ def __init__(self, path: str) -> None:
22
+ super().__init__(
23
+ f"Paths in BEANCOUNT_FILE need to be absolute: {path}"
24
+ )
25
+
26
+
27
+ class NoFileSpecifiedError(click.UsageError): # noqa: D101
28
+ def __init__(self) -> None: # pragma: no cover
29
+ super().__init__("No file specified")
30
+
31
+
32
+ def _add_env_filenames(filenames: tuple[str, ...]) -> tuple[str, ...]:
33
+ """Read additional filenames from BEANCOUNT_FILE."""
34
+ env_filename = os.environ.get("BEANCOUNT_FILE")
35
+ if not env_filename:
36
+ return tuple(dict.fromkeys(filenames))
37
+
38
+ env_names = env_filename.split(os.pathsep)
39
+ for name in env_names:
40
+ if not Path(name).is_absolute():
41
+ raise NonAbsolutePathError(name)
42
+
43
+ all_names = tuple(env_names) + filenames
44
+ return tuple(dict.fromkeys(all_names))
45
+
46
+
47
+ @click.command(context_settings={"auto_envvar_prefix": "RUSTFAVA"})
48
+ @click.argument(
49
+ "filenames",
50
+ nargs=-1,
51
+ type=click.Path(exists=True, dir_okay=False, resolve_path=True),
52
+ )
53
+ @click.option(
54
+ "-p",
55
+ "--port",
56
+ type=int,
57
+ default=5000,
58
+ show_default=True,
59
+ metavar="<port>",
60
+ help="The port to listen on.",
61
+ )
62
+ @click.option(
63
+ "-H",
64
+ "--host",
65
+ type=str,
66
+ default="localhost",
67
+ show_default=True,
68
+ metavar="<host>",
69
+ help="The host to listen on.",
70
+ )
71
+ @click.option("--prefix", type=str, help="Set an URL prefix.")
72
+ @click.option(
73
+ "--incognito",
74
+ is_flag=True,
75
+ help="Run in incognito mode and obscure all numbers.",
76
+ )
77
+ @click.option(
78
+ "--read-only",
79
+ is_flag=True,
80
+ help="Run in read-only mode, disable any change through rustfava.",
81
+ )
82
+ @click.option("-d", "--debug", is_flag=True, help="Turn on debugging.")
83
+ @click.option(
84
+ "--profile",
85
+ is_flag=True,
86
+ help="Turn on profiling. Implies --debug.",
87
+ )
88
+ @click.option(
89
+ "--profile-dir",
90
+ type=click.Path(),
91
+ help="Output directory for profiling data.",
92
+ )
93
+ @click.option(
94
+ "--poll-watcher", is_flag=True, help="Use old polling-based watcher."
95
+ )
96
+ @click.version_option(package_name="rustfava")
97
+ def main( # noqa: PLR0913
98
+ *,
99
+ filenames: tuple[str, ...] = (),
100
+ port: int = 5000,
101
+ host: str = "localhost",
102
+ prefix: str | None = None,
103
+ incognito: bool = False,
104
+ read_only: bool = False,
105
+ debug: bool = False,
106
+ profile: bool = False,
107
+ profile_dir: str | None = None,
108
+ poll_watcher: bool = False,
109
+ ) -> None: # pragma: no cover
110
+ """Start Rustfava for FILENAMES on http://<host>:<port>.
111
+
112
+ If the `BEANCOUNT_FILE` environment variable is set, Rustfava will use the
113
+ files (delimited by ';' on Windows and ':' on POSIX) given there in
114
+ addition to FILENAMES.
115
+
116
+ Note you can also specify command-line options via environment variables
117
+ with the `RUSTFAVA_` prefix. For example, `--host=0.0.0.0` is equivalent to
118
+ setting the environment variable `RUSTFAVA_HOST=0.0.0.0`.
119
+ """
120
+ all_filenames = _add_env_filenames(filenames)
121
+
122
+ if not all_filenames:
123
+ raise NoFileSpecifiedError
124
+
125
+ from rustfava.application import create_app
126
+
127
+ app = create_app(
128
+ all_filenames,
129
+ incognito=incognito,
130
+ read_only=read_only,
131
+ poll_watcher=poll_watcher,
132
+ )
133
+
134
+ if prefix:
135
+ from werkzeug.middleware.dispatcher import DispatcherMiddleware
136
+
137
+ from rustfava.util import simple_wsgi
138
+
139
+ app.wsgi_app = DispatcherMiddleware( # type: ignore[method-assign]
140
+ simple_wsgi,
141
+ {prefix: app.wsgi_app},
142
+ )
143
+
144
+ # ensure that cheroot does not use IP6 for localhost
145
+ host = "127.0.0.1" if host == "localhost" else host
146
+ # Debug mode if profiling is active
147
+ debug = debug or profile
148
+
149
+ click.secho(f"Starting Fava on http://{host}:{port}", fg="green")
150
+ if not debug:
151
+ from cheroot.wsgi import Server
152
+
153
+ server = Server((host, port), app)
154
+ try:
155
+ server.start()
156
+ except KeyboardInterrupt:
157
+ click.echo("Keyboard interrupt received: stopping Fava", err=True)
158
+ server.stop()
159
+ except OSError as error:
160
+ if "No socket could be created" in str(error):
161
+ raise AddressInUse(port) from error
162
+ raise click.Abort from error
163
+ else:
164
+ from werkzeug.middleware.profiler import ProfilerMiddleware
165
+
166
+ from rustfava.util import setup_debug_logging
167
+
168
+ setup_debug_logging()
169
+ if profile:
170
+ app.wsgi_app = ProfilerMiddleware( # type: ignore[method-assign]
171
+ app.wsgi_app,
172
+ restrictions=(30,),
173
+ profile_dir=profile_dir or None,
174
+ )
175
+
176
+ app.jinja_env.auto_reload = True
177
+ try:
178
+ app.run(host, port, debug)
179
+ except OSError as error:
180
+ if error.errno == errno.EADDRINUSE:
181
+ raise AddressInUse(port) from error
182
+ raise
183
+
184
+
185
+ # needed for pyinstaller:
186
+ if __name__ == "__main__": # pragma: no cover
187
+ main()
rustfava/context.py ADDED
@@ -0,0 +1,13 @@
1
+ """Specify types for the flask application context."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from flask import g as flask_g
8
+
9
+ if TYPE_CHECKING: # pragma: no cover
10
+ from rustfava._ctx_globals_class import Context
11
+
12
+
13
+ g: Context = flask_g # type: ignore[assignment]