tradedangerous 12.7.6__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.
- py.typed +1 -0
- trade.py +49 -0
- tradedangerous/__init__.py +43 -0
- tradedangerous/cache.py +1381 -0
- tradedangerous/cli.py +136 -0
- tradedangerous/commands/TEMPLATE.py +74 -0
- tradedangerous/commands/__init__.py +244 -0
- tradedangerous/commands/buildcache_cmd.py +102 -0
- tradedangerous/commands/buy_cmd.py +427 -0
- tradedangerous/commands/commandenv.py +372 -0
- tradedangerous/commands/exceptions.py +94 -0
- tradedangerous/commands/export_cmd.py +150 -0
- tradedangerous/commands/import_cmd.py +222 -0
- tradedangerous/commands/local_cmd.py +243 -0
- tradedangerous/commands/market_cmd.py +207 -0
- tradedangerous/commands/nav_cmd.py +252 -0
- tradedangerous/commands/olddata_cmd.py +270 -0
- tradedangerous/commands/parsing.py +221 -0
- tradedangerous/commands/rares_cmd.py +298 -0
- tradedangerous/commands/run_cmd.py +1521 -0
- tradedangerous/commands/sell_cmd.py +262 -0
- tradedangerous/commands/shipvendor_cmd.py +60 -0
- tradedangerous/commands/station_cmd.py +68 -0
- tradedangerous/commands/trade_cmd.py +181 -0
- tradedangerous/commands/update_cmd.py +67 -0
- tradedangerous/corrections.py +55 -0
- tradedangerous/csvexport.py +234 -0
- tradedangerous/db/__init__.py +27 -0
- tradedangerous/db/adapter.py +192 -0
- tradedangerous/db/config.py +107 -0
- tradedangerous/db/engine.py +259 -0
- tradedangerous/db/lifecycle.py +332 -0
- tradedangerous/db/locks.py +208 -0
- tradedangerous/db/orm_models.py +500 -0
- tradedangerous/db/paths.py +113 -0
- tradedangerous/db/utils.py +661 -0
- tradedangerous/edscupdate.py +565 -0
- tradedangerous/edsmupdate.py +474 -0
- tradedangerous/formatting.py +210 -0
- tradedangerous/fs.py +156 -0
- tradedangerous/gui.py +1146 -0
- tradedangerous/mapping.py +133 -0
- tradedangerous/mfd/__init__.py +103 -0
- tradedangerous/mfd/saitek/__init__.py +3 -0
- tradedangerous/mfd/saitek/directoutput.py +678 -0
- tradedangerous/mfd/saitek/x52pro.py +195 -0
- tradedangerous/misc/checkpricebounds.py +287 -0
- tradedangerous/misc/clipboard.py +49 -0
- tradedangerous/misc/coord64.py +83 -0
- tradedangerous/misc/csvdialect.py +57 -0
- tradedangerous/misc/derp-sentinel.py +35 -0
- tradedangerous/misc/diff-system-csvs.py +159 -0
- tradedangerous/misc/eddb.py +81 -0
- tradedangerous/misc/eddn.py +349 -0
- tradedangerous/misc/edsc.py +437 -0
- tradedangerous/misc/edsm.py +121 -0
- tradedangerous/misc/importeddbstats.py +54 -0
- tradedangerous/misc/prices-json-exp.py +179 -0
- tradedangerous/misc/progress.py +194 -0
- tradedangerous/plugins/__init__.py +249 -0
- tradedangerous/plugins/edcd_plug.py +371 -0
- tradedangerous/plugins/eddblink_plug.py +861 -0
- tradedangerous/plugins/edmc_batch_plug.py +133 -0
- tradedangerous/plugins/spansh_plug.py +2647 -0
- tradedangerous/prices.py +211 -0
- tradedangerous/submit-distances.py +422 -0
- tradedangerous/templates/Added.csv +37 -0
- tradedangerous/templates/Category.csv +17 -0
- tradedangerous/templates/RareItem.csv +143 -0
- tradedangerous/templates/TradeDangerous.sql +338 -0
- tradedangerous/tools.py +40 -0
- tradedangerous/tradecalc.py +1302 -0
- tradedangerous/tradedb.py +2320 -0
- tradedangerous/tradeenv.py +313 -0
- tradedangerous/tradeenv.pyi +109 -0
- tradedangerous/tradeexcept.py +131 -0
- tradedangerous/tradeorm.py +183 -0
- tradedangerous/transfers.py +192 -0
- tradedangerous/utils.py +243 -0
- tradedangerous/version.py +16 -0
- tradedangerous-12.7.6.dist-info/METADATA +106 -0
- tradedangerous-12.7.6.dist-info/RECORD +87 -0
- tradedangerous-12.7.6.dist-info/WHEEL +5 -0
- tradedangerous-12.7.6.dist-info/entry_points.txt +3 -0
- tradedangerous-12.7.6.dist-info/licenses/LICENSE +373 -0
- tradedangerous-12.7.6.dist-info/top_level.txt +2 -0
- tradegui.py +24 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# The runtime environment TD tools are expected to run with is encapsulated
|
|
2
|
+
# into a single object, the TradeEnv. See TradeEnv docstring for more.
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import traceback
|
|
9
|
+
import typing
|
|
10
|
+
|
|
11
|
+
# Import some utilities from the 'rich' library that provide ways to colorize and animate
|
|
12
|
+
# the console output, along with other useful features.
|
|
13
|
+
# If the user has 'EXCEPTIONS' defined to something in the environment, then we can
|
|
14
|
+
# immediately benefit from beautified stacktraces.
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
from rich.traceback import install as install_rich_traces
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if typing.TYPE_CHECKING:
|
|
20
|
+
import argparse
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_ROOT = os.path.abspath(os.path.dirname(__file__))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Create a single instance of the console for everyone to use, unless they really
|
|
28
|
+
# want to do something unusual.
|
|
29
|
+
CONSOLE = Console()
|
|
30
|
+
STDERR = Console(stderr=True)
|
|
31
|
+
|
|
32
|
+
if os.getenv("EXCEPTIONS"):
|
|
33
|
+
# This makes call stacks show additional context and do syntax highlighting
|
|
34
|
+
# that can turn reading a callstack from hours into seconds.
|
|
35
|
+
install_rich_traces(console=STDERR, show_locals=False, extra_lines=1)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BaseColorTheme:
|
|
39
|
+
""" A way to theme the console output colors. The default is none. """
|
|
40
|
+
CLOSE: str = "" # code to stop the last color
|
|
41
|
+
dim: str = "" # code to make text dim
|
|
42
|
+
bold: str = "" # code to make text bold
|
|
43
|
+
italic: str = "" # code to make text italic
|
|
44
|
+
# blink: NEVER = "don't you dare"
|
|
45
|
+
|
|
46
|
+
# style, label
|
|
47
|
+
debug, DEBUG = dim, "#"
|
|
48
|
+
note, NOTE = bold, "NOTE"
|
|
49
|
+
info, INFO = "", "INFO"
|
|
50
|
+
warn, WARN = "", "WARNING"
|
|
51
|
+
|
|
52
|
+
seq_first: str = "" # the first item in a sequence
|
|
53
|
+
seq_last: str = "" # the last item in a sequence
|
|
54
|
+
|
|
55
|
+
# Included as examples of how you might use this to manipulate tradecal output.
|
|
56
|
+
itm_units: str = "" # the amount of something
|
|
57
|
+
itm_name: str = "" # name of that unit
|
|
58
|
+
itm_price: str = "" # how much does it cost?
|
|
59
|
+
|
|
60
|
+
def render(self, renderable: Any, style: str) -> str: # pragma: no cover, pylint: disable=unused-argument
|
|
61
|
+
""" Renders the given printable item with the given style; BaseColorTheme simply uses a string transformation. """
|
|
62
|
+
if isinstance(renderable, str):
|
|
63
|
+
return renderable # avoid an allocation
|
|
64
|
+
return str(renderable)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class BasicRichColorTheme(BaseColorTheme):
|
|
68
|
+
""" Provide's 'rich' styling without our own colorization. """
|
|
69
|
+
CLOSE = "[/]"
|
|
70
|
+
bold = "[bold]"
|
|
71
|
+
dim = "[dim]"
|
|
72
|
+
italic = "[italic]"
|
|
73
|
+
|
|
74
|
+
# style, label
|
|
75
|
+
debug, DEBUG = dim, "#"
|
|
76
|
+
note, NOTE = bold, "NOTE"
|
|
77
|
+
info, INFO = "", "INFO"
|
|
78
|
+
warn, WARN = "[orange3]", "WARNING"
|
|
79
|
+
|
|
80
|
+
def render(self, renderable: Any, style: str) -> str: # pragma: no cover
|
|
81
|
+
style_attr = getattr(self, style, "")
|
|
82
|
+
if not style_attr:
|
|
83
|
+
return renderable if isinstance(renderable, str) else str(renderable)
|
|
84
|
+
return f"{style_attr}{renderable}{self.CLOSE}"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class RichColorTheme(BasicRichColorTheme):
|
|
88
|
+
""" Demonstrates how you might augment the rich theme with colors to be used fin e.g tradecal. """
|
|
89
|
+
DEBUG = ":spider_web:"
|
|
90
|
+
NOTE = ":information_source:"
|
|
91
|
+
WARN = ":warning:"
|
|
92
|
+
INFO = ":gear:"
|
|
93
|
+
|
|
94
|
+
# e.g. First station
|
|
95
|
+
seq_first = "[cyan]"
|
|
96
|
+
# e.g. Last station
|
|
97
|
+
seq_last = "[blue]"
|
|
98
|
+
|
|
99
|
+
# Included as examples of how you might use this to manipulate tradecal output.
|
|
100
|
+
itm_units = "[yellow3]"
|
|
101
|
+
itm_name = "[yellow]"
|
|
102
|
+
itm_price = "[bold]"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class BaseConsoleIOMixin:
|
|
106
|
+
""" Base mixin for running output through rich. """
|
|
107
|
+
color: bool
|
|
108
|
+
console: Console
|
|
109
|
+
debug: int
|
|
110
|
+
detail: int
|
|
111
|
+
encoding: str
|
|
112
|
+
quiet: int
|
|
113
|
+
stderr: Console
|
|
114
|
+
theme: BaseColorTheme
|
|
115
|
+
|
|
116
|
+
def uprint(self, *args: Any, stderr: bool = False, style: str | None = None, **kwargs: Any) -> None:
|
|
117
|
+
"""
|
|
118
|
+
unicode-safe print via console or stderr, with 'rich' markup handling.
|
|
119
|
+
"""
|
|
120
|
+
console = self.stderr if stderr else self.console
|
|
121
|
+
console.print(*args, style=style, **kwargs)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class NonUtf8ConsoleIOMixin(BaseConsoleIOMixin):
|
|
125
|
+
""" Mixing for running output through rich with UTF8-translation smoothing. """
|
|
126
|
+
def uprint(self, *args: Any, stderr: bool = False, style: str | None = None, **kwargs: Any) -> None:
|
|
127
|
+
""" unicode-handling print: when the stdout stream is not utf-8 supporting,
|
|
128
|
+
we do a little extra io work to ensure users don't get confusing unicode
|
|
129
|
+
errors. When the output stream *is* utf-8.
|
|
130
|
+
|
|
131
|
+
:param stderr: report to stderr instead of stdout
|
|
132
|
+
:param style: specify a 'rich' console style to use when the stream supports it
|
|
133
|
+
"""
|
|
134
|
+
console = self.stderr if stderr else self.console
|
|
135
|
+
try:
|
|
136
|
+
# Attempt to print; the 'file' argument isn't supported by rich, so we'll
|
|
137
|
+
# need to fall-back on old print when someone specifies it.
|
|
138
|
+
console.print(*args, style=style, **kwargs)
|
|
139
|
+
|
|
140
|
+
except UnicodeEncodeError as e:
|
|
141
|
+
# Characters in the output couldn't be translated to unicode.
|
|
142
|
+
if not self.quiet:
|
|
143
|
+
self.stderr.print(
|
|
144
|
+
f"{self.theme.WARN}{self.theme.bold}CAUTION: Your terminal/console couldn't handle some "
|
|
145
|
+
"text I tried to print."
|
|
146
|
+
)
|
|
147
|
+
if 'EXCEPTIONS' in os.environ:
|
|
148
|
+
traceback.print_exc()
|
|
149
|
+
else:
|
|
150
|
+
self.stderr.print(e)
|
|
151
|
+
|
|
152
|
+
# Try to translate each ary into a viable string using utf error-replacement.
|
|
153
|
+
components = [
|
|
154
|
+
str(arg)
|
|
155
|
+
.encode(TradeEnv.encoding, errors="replace")
|
|
156
|
+
.decode(TradeEnv.encoding)
|
|
157
|
+
for arg in args
|
|
158
|
+
]
|
|
159
|
+
console.print(*components, style=style, **kwargs)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
ENV_DEFAULTS: dict[str, Any] = {
|
|
163
|
+
'debug': 0,
|
|
164
|
+
'detail': 0,
|
|
165
|
+
'quiet': 0,
|
|
166
|
+
'color': False,
|
|
167
|
+
'theme': BaseColorTheme(),
|
|
168
|
+
'persist': bool(os.environ.get('TD_PERSIST', '1')), # Use the 'persistence' mechanimsm
|
|
169
|
+
'dataDir': os.environ.get('TD_DATA') or os.path.join(os.getcwd(), 'data'),
|
|
170
|
+
'csvDir': os.environ.get('TD_CSV') or os.environ.get('TD_DATA') or os.path.join(os.getcwd(), 'data'),
|
|
171
|
+
'tmpDir': os.environ.get('TD_TMP') or os.path.join(os.getcwd(), 'tmp'),
|
|
172
|
+
'templateDir': os.path.join(_ROOT, 'templates'),
|
|
173
|
+
'cwDir': os.getcwd(),
|
|
174
|
+
'console': CONSOLE,
|
|
175
|
+
'stderr': STDERR,
|
|
176
|
+
'maxSystemLinkLy': 64.0,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# If the console doesn't support UTF8, use the more-complicated implementation.
|
|
181
|
+
if str(sys.stdout.encoding).upper() != 'UTF-8':
|
|
182
|
+
Utf8SafeConsoleIOMixin = NonUtf8ConsoleIOMixin
|
|
183
|
+
else:
|
|
184
|
+
Utf8SafeConsoleIOMixin = BaseConsoleIOMixin
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TradeEnv(Utf8SafeConsoleIOMixin):
|
|
188
|
+
"""
|
|
189
|
+
TradeDangerous provides a container for runtime configuration (cli flags, etc) and io operations to
|
|
190
|
+
enable normalization of things without having to pass huge sets of arguments. This includes things
|
|
191
|
+
like logging and reporting functionality.
|
|
192
|
+
|
|
193
|
+
To print debug lines, use DEBUG<N>, e.g. DEBUG0, which takes a format() string and parameters, e.g.
|
|
194
|
+
DEBUG1("hello, {world}{}", "!", world="world")
|
|
195
|
+
|
|
196
|
+
is similar to:
|
|
197
|
+
arg0, arg1 = "!", "world"
|
|
198
|
+
if tdenv.debug > 1:
|
|
199
|
+
tdenv.console.print("# hello, {arg1}{}".format(arg0=arg0, arg1=arg1))
|
|
200
|
+
|
|
201
|
+
Use "NOTE" to print remarks which can be disabled with -q.
|
|
202
|
+
"""
|
|
203
|
+
csvDir: str
|
|
204
|
+
cwDir: str
|
|
205
|
+
dataDir: str
|
|
206
|
+
maxSystemLinkLy: float
|
|
207
|
+
persist: bool
|
|
208
|
+
templateDir: str
|
|
209
|
+
theme: BaseColorTheme
|
|
210
|
+
tmpDir: str
|
|
211
|
+
|
|
212
|
+
encoding = sys.stdout.encoding
|
|
213
|
+
|
|
214
|
+
def __init__(self, properties: dict[str, typing.Any] | argparse.Namespace | None = None, **kwargs: Any) -> None:
|
|
215
|
+
# Inject the defaults into ourselves in a dict-like way
|
|
216
|
+
self.__dict__.update(ENV_DEFAULTS)
|
|
217
|
+
|
|
218
|
+
# If properties is a namespace, extract the dictionary; otherwise use it as-is
|
|
219
|
+
if properties and hasattr(properties, '__dict__'): # which arparse.Namespace has
|
|
220
|
+
properties = properties.__dict__
|
|
221
|
+
# Merge into our dictionary
|
|
222
|
+
self.__dict__.update(properties or {})
|
|
223
|
+
|
|
224
|
+
# Merge the kwargs dictionary
|
|
225
|
+
self.__dict__.update(kwargs or {})
|
|
226
|
+
|
|
227
|
+
# When debugging has been enabled on startup, enable slightly more
|
|
228
|
+
# verbose rich backtraces.
|
|
229
|
+
if self.__dict__['debug']:
|
|
230
|
+
install_rich_traces(console=STDERR, show_locals=True, extra_lines=2)
|
|
231
|
+
|
|
232
|
+
self.theme = RichColorTheme() if self.__dict__['color'] else BasicRichColorTheme()
|
|
233
|
+
|
|
234
|
+
@staticmethod
|
|
235
|
+
def __disabled_uprint(*args: Any, **kwargs: Any) -> None:
|
|
236
|
+
pass
|
|
237
|
+
|
|
238
|
+
def __getattr__(self, key: str) -> Any:
|
|
239
|
+
""" Return the default for attributes we don't have """
|
|
240
|
+
# The first time the DEBUG attribute is referenced, register a method for it.
|
|
241
|
+
disabled: bool = False
|
|
242
|
+
theme_prefix: str | None = None
|
|
243
|
+
theme_label: str | None = None
|
|
244
|
+
match key:
|
|
245
|
+
case "WARN" if self.quiet > 1:
|
|
246
|
+
disabled = True
|
|
247
|
+
case "WARN":
|
|
248
|
+
theme_prefix, theme_label = self.theme.warn, self.theme.WARN
|
|
249
|
+
case "NOTE" | "INFO" if self.quiet:
|
|
250
|
+
disabled = True
|
|
251
|
+
case "NOTE":
|
|
252
|
+
theme_prefix, theme_label = self.theme.note, self.theme.NOTE
|
|
253
|
+
case "INFO":
|
|
254
|
+
theme_prefix, theme_label = self.theme.info, self.theme.INFO
|
|
255
|
+
case _ if key.startswith("DEBUG") and int(key[5:]) >= self.debug:
|
|
256
|
+
disabled = True
|
|
257
|
+
case _ if key.startswith("DEBUG"):
|
|
258
|
+
theme_prefix, theme_label = self.theme.debug, self.theme.DEBUG + key[5:]
|
|
259
|
+
case _:
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
# If there's no function but there's a theme, create a function
|
|
263
|
+
if disabled:
|
|
264
|
+
setattr(self, key, self.__disabled_uprint)
|
|
265
|
+
return self.__disabled_uprint
|
|
266
|
+
|
|
267
|
+
if theme_prefix is not None:
|
|
268
|
+
def __log_helper(outText: str, *args: Any, stderr: bool = False, **kwargs: Any):
|
|
269
|
+
try:
|
|
270
|
+
msg = str(outText) if not (args or kwargs) else str(outText).format(*args, **kwargs)
|
|
271
|
+
except Exception: # noqa # pylint: disable=broad-except
|
|
272
|
+
# Fallback: dump raw message + args/kwargs repr
|
|
273
|
+
msg = f"{outText} {args!r} {kwargs!r}"
|
|
274
|
+
|
|
275
|
+
self.uprint(f"{theme_prefix}{theme_label}: {msg}", stderr=stderr)
|
|
276
|
+
|
|
277
|
+
setattr(self, key, __log_helper)
|
|
278
|
+
return __log_helper
|
|
279
|
+
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
def remove_file(self, *args: str | Path) -> bool:
|
|
283
|
+
""" Unlinks a file, as long as it exists, and logs the action at level 1. """
|
|
284
|
+
path = Path(*args)
|
|
285
|
+
if not path.exists():
|
|
286
|
+
return False
|
|
287
|
+
path.unlink()
|
|
288
|
+
self.DEBUG1(":cross_mark: deleted {}", path)
|
|
289
|
+
return True
|
|
290
|
+
|
|
291
|
+
def rename_file(self, *, old: str | Path, new: str | Path) -> bool:
|
|
292
|
+
"""
|
|
293
|
+
If 'new' exists, deletes it, and then attempts to rename old -> new. If new is not specified,
|
|
294
|
+
then '.old' is appended to the end of the old filename while retaining the original suffix.
|
|
295
|
+
|
|
296
|
+
:param old: The current path/name of the file.
|
|
297
|
+
:param new: The path/name to rename the file to and remove before attempting.
|
|
298
|
+
:returns: True if the file existed and was renamed.
|
|
299
|
+
"""
|
|
300
|
+
# Promote new to a guaranteed Path and remove it if it's present.
|
|
301
|
+
new = Path(new)
|
|
302
|
+
self.remove_file(new)
|
|
303
|
+
|
|
304
|
+
# Promote new to a guaranteed Path and confirm it exists.
|
|
305
|
+
old = Path(old)
|
|
306
|
+
if not old.exists():
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
# Perform the rename and log it at level 1.
|
|
310
|
+
old.rename(new)
|
|
311
|
+
self.DEBUG1(":recycle: moved {} to {}", old, new)
|
|
312
|
+
|
|
313
|
+
return True
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# pylint: disable=multiple-statements-on-one-line
|
|
2
|
+
# flake8: noqa: E704
|
|
3
|
+
#
|
|
4
|
+
# Because TradeEnv on-the-fly constructs the various logging methods
|
|
5
|
+
# etc, most IDEs and linters struggle with the types of many of its
|
|
6
|
+
# methods and built-in properties.
|
|
7
|
+
#
|
|
8
|
+
# This is a python header unit that forward declares stuff so that
|
|
9
|
+
# the IDEs are less of a wall of squiggles.
|
|
10
|
+
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
import argparse
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseColorTheme:
|
|
18
|
+
CLOSE: str
|
|
19
|
+
dim: str
|
|
20
|
+
bold: str
|
|
21
|
+
italic: str
|
|
22
|
+
debug: str
|
|
23
|
+
DEBUG: str
|
|
24
|
+
note: str
|
|
25
|
+
NOTE: str
|
|
26
|
+
info: str
|
|
27
|
+
INFO: str
|
|
28
|
+
warn: str
|
|
29
|
+
WARN: str
|
|
30
|
+
seq_first: str
|
|
31
|
+
seq_last: str
|
|
32
|
+
itm_units: str
|
|
33
|
+
itm_name: str
|
|
34
|
+
itm_price: str
|
|
35
|
+
def render(self, renderable: Any, style: str) -> str: ...
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class BasicRichColorTheme(BaseColorTheme):
|
|
39
|
+
CLOSE: str
|
|
40
|
+
bold: str
|
|
41
|
+
dim: str
|
|
42
|
+
italic: str
|
|
43
|
+
debug: str
|
|
44
|
+
DEBUG: str
|
|
45
|
+
note: str
|
|
46
|
+
NOTE: str
|
|
47
|
+
info: str
|
|
48
|
+
INFO: str
|
|
49
|
+
warn: str
|
|
50
|
+
WARN: str
|
|
51
|
+
def render(self, renderable: Any, style: str) -> str: ...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class RichColorTheme(BasicRichColorTheme):
|
|
55
|
+
DEBUG: str
|
|
56
|
+
NOTE: str
|
|
57
|
+
WARN: str
|
|
58
|
+
INFO: str
|
|
59
|
+
seq_first: str
|
|
60
|
+
seq_last: str
|
|
61
|
+
itm_units: str
|
|
62
|
+
itm_name: str
|
|
63
|
+
itm_price: str
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class BaseConsoleIOMixin:
|
|
67
|
+
color: bool
|
|
68
|
+
console: Console
|
|
69
|
+
debug: int
|
|
70
|
+
detail: int
|
|
71
|
+
encoding: str
|
|
72
|
+
quiet: int
|
|
73
|
+
stderr: Console
|
|
74
|
+
theme: BaseColorTheme
|
|
75
|
+
|
|
76
|
+
def uprint(self, *args: Any, stderr: bool = False, style: str | None = None, **kwargs: Any) -> None: ...
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class NonUtf8ConsoleIOMixin(BaseConsoleIOMixin):
|
|
80
|
+
def uprint(self, *args: Any, stderr: bool = False, style: str | None = None, **kwargs: Any) -> None: ...
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TradeEnv(BaseConsoleIOMixin):
|
|
84
|
+
csvDir: str
|
|
85
|
+
cwDir: str
|
|
86
|
+
dataDir: str
|
|
87
|
+
maxSystemLinkLy: float
|
|
88
|
+
persist: bool
|
|
89
|
+
templateDir: str
|
|
90
|
+
theme: BaseColorTheme
|
|
91
|
+
tmpDir: str
|
|
92
|
+
|
|
93
|
+
def __init__(self, properties: dict[str, Any] | argparse.Namespace | None = None, **kwargs: Any) -> None: ...
|
|
94
|
+
|
|
95
|
+
# Dynamically-generated log methods with full type hints
|
|
96
|
+
def DEBUG0(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
97
|
+
def DEBUG1(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
98
|
+
def DEBUG2(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
99
|
+
def DEBUG3(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
100
|
+
def DEBUG4(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
101
|
+
def INFO(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
102
|
+
def NOTE(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
103
|
+
def WARN(self, outText: str, *args: Any, stderr: bool = False, **kwargs: Any) -> None: ...
|
|
104
|
+
|
|
105
|
+
# File operations
|
|
106
|
+
def remove_file(self, *args: str | Path) -> bool: ...
|
|
107
|
+
def rename_file(self, *, old: str | Path, new: str | Path) -> bool: ...
|
|
108
|
+
|
|
109
|
+
def uprint(self, *args: Any, stderr: bool = False, style: str | None = None, **kwargs: Any) -> None: ...
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
tradeexcept defines standard exceptions used within TradeDangerous.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
import typing
|
|
6
|
+
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
try:
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
except ImportError:
|
|
11
|
+
from typing import Callable
|
|
12
|
+
from typing import Any
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
AMBIGUITY_LIMIT = 6
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SimpleAbort(Exception):
|
|
20
|
+
"""
|
|
21
|
+
SimpleAbort is Exception but can be caught and presented without
|
|
22
|
+
any kind of backtrace.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TradeException(Exception):
|
|
27
|
+
"""
|
|
28
|
+
Distinguishes runtime logical errors (such as no data for what you
|
|
29
|
+
queried) from programmatic errors (such as Oliver accessing a hash
|
|
30
|
+
with the wrong type of key).
|
|
31
|
+
|
|
32
|
+
TradeExcepts should be caught by the program and displayed in the
|
|
33
|
+
most user friendly way possible.
|
|
34
|
+
"""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MissingDB(TradeException):
|
|
39
|
+
"""
|
|
40
|
+
Reports that the database is missing in a scenario where it is
|
|
41
|
+
required and not default-created for the user.
|
|
42
|
+
|
|
43
|
+
Ideally, this should describe to the user how to create the
|
|
44
|
+
database, perhaps through a "bootstrap" subcommand.
|
|
45
|
+
"""
|
|
46
|
+
def __init__(self, dbpath: str | Path):
|
|
47
|
+
super().__init__(
|
|
48
|
+
f"{dbpath}: Data file(s) are missing, you must initialize the database first. "
|
|
49
|
+
"Consider using `trade import -P eddblink -O bootstrap` or if you are "
|
|
50
|
+
"managing data by hand, use the buildcache subcommand."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
class AmbiguityError(TradeException):
|
|
54
|
+
"""
|
|
55
|
+
Raised when a search key could match multiple entities.
|
|
56
|
+
Attributes:
|
|
57
|
+
lookupType - description of what was being queried,
|
|
58
|
+
searchKey - the key given to the search routine,
|
|
59
|
+
anyMatch - list of items which were found to match, if any
|
|
60
|
+
key - retrieve the display string for a candidate
|
|
61
|
+
"""
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
lookupType: str,
|
|
65
|
+
searchKey: str,
|
|
66
|
+
anyMatch: list[Any],
|
|
67
|
+
key: Callable[[Any], str] = lambda item: item
|
|
68
|
+
) -> None:
|
|
69
|
+
self.lookupType = lookupType
|
|
70
|
+
self.searchKey = searchKey
|
|
71
|
+
self.anyMatch = anyMatch
|
|
72
|
+
self.key = key
|
|
73
|
+
|
|
74
|
+
def __str__(self) -> str:
|
|
75
|
+
anyMatch, key = self.anyMatch, self.key
|
|
76
|
+
|
|
77
|
+
# ------------------------------------------------------------------
|
|
78
|
+
# Special-case: system name collisions where we passed in
|
|
79
|
+
# (index, System) pairs from TradeDB.lookupSystem.
|
|
80
|
+
# ------------------------------------------------------------------
|
|
81
|
+
if (
|
|
82
|
+
self.lookupType == "System"
|
|
83
|
+
and anyMatch
|
|
84
|
+
and isinstance(anyMatch[0], tuple)
|
|
85
|
+
and len(anyMatch[0]) >= 2
|
|
86
|
+
):
|
|
87
|
+
lines = [
|
|
88
|
+
f'System name "{self.searchKey}" refers to more than one distinct system.',
|
|
89
|
+
"",
|
|
90
|
+
'Select the one you intended using "@N":',
|
|
91
|
+
"",
|
|
92
|
+
]
|
|
93
|
+
for index, system in anyMatch:
|
|
94
|
+
# Be tolerant in case the contents are not exactly (int, System)
|
|
95
|
+
try:
|
|
96
|
+
name = system.dbname
|
|
97
|
+
x, y, z = system.posX, system.posY, system.posZ
|
|
98
|
+
lines.append(
|
|
99
|
+
f" {name}@{index} 45 ({x:.1f}, {y:.1f}, {z:.1f})"
|
|
100
|
+
)
|
|
101
|
+
except Exception:
|
|
102
|
+
# Fallback to the provided key() formatter
|
|
103
|
+
lines.append(f" {key((index, system))}")
|
|
104
|
+
lines.append("")
|
|
105
|
+
lines.append("(Index numbers are ordered by Galactic X coordinate.)")
|
|
106
|
+
return "\n".join(lines)
|
|
107
|
+
|
|
108
|
+
# ------------------------------------------------------------------
|
|
109
|
+
# Generic ambiguity formatting used everywhere else
|
|
110
|
+
# ------------------------------------------------------------------
|
|
111
|
+
if not anyMatch:
|
|
112
|
+
# Not matching anything is not "ambiguous".
|
|
113
|
+
raise RuntimeError('called AmbiguityError with no matches')
|
|
114
|
+
|
|
115
|
+
# Truncate the list of candidates so we don't show more than 10
|
|
116
|
+
candidates = [key(c) for c in anyMatch[:AMBIGUITY_LIMIT]]
|
|
117
|
+
if len(anyMatch) < 3:
|
|
118
|
+
opportunities = " or ".join(candidates)
|
|
119
|
+
else:
|
|
120
|
+
if len(anyMatch) > AMBIGUITY_LIMIT:
|
|
121
|
+
candidates[-1] = "..."
|
|
122
|
+
else:
|
|
123
|
+
candidates[-1] = "or " + candidates[-1] # oxford comma
|
|
124
|
+
opportunities = ", ".join(candidates)
|
|
125
|
+
|
|
126
|
+
return f'{self.lookupType} "{self.searchKey}" could match {opportunities}'
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class SystemNotStationError(TradeException):
|
|
130
|
+
""" Raised when a station lookup matched a System but
|
|
131
|
+
could not be automatically reduced to a Station. """
|