tradedangerous 10.17.0__py3-none-any.whl → 11.0.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.

Potentially problematic release.


This version of tradedangerous might be problematic. Click here for more details.

Files changed (74) hide show
  1. tradedangerous/__init__.py +4 -4
  2. tradedangerous/cache.py +183 -148
  3. tradedangerous/cli.py +2 -7
  4. tradedangerous/commands/TEMPLATE.py +1 -2
  5. tradedangerous/commands/__init__.py +2 -4
  6. tradedangerous/commands/buildcache_cmd.py +6 -11
  7. tradedangerous/commands/buy_cmd.py +11 -12
  8. tradedangerous/commands/commandenv.py +16 -15
  9. tradedangerous/commands/exceptions.py +6 -4
  10. tradedangerous/commands/export_cmd.py +2 -4
  11. tradedangerous/commands/import_cmd.py +3 -5
  12. tradedangerous/commands/local_cmd.py +16 -25
  13. tradedangerous/commands/market_cmd.py +9 -8
  14. tradedangerous/commands/nav_cmd.py +17 -25
  15. tradedangerous/commands/olddata_cmd.py +9 -15
  16. tradedangerous/commands/parsing.py +9 -6
  17. tradedangerous/commands/rares_cmd.py +9 -10
  18. tradedangerous/commands/run_cmd.py +25 -26
  19. tradedangerous/commands/sell_cmd.py +9 -9
  20. tradedangerous/commands/shipvendor_cmd.py +4 -7
  21. tradedangerous/commands/station_cmd.py +8 -14
  22. tradedangerous/commands/trade_cmd.py +5 -10
  23. tradedangerous/commands/update_cmd.py +10 -7
  24. tradedangerous/commands/update_gui.py +1 -3
  25. tradedangerous/corrections.py +1 -3
  26. tradedangerous/csvexport.py +8 -8
  27. tradedangerous/edscupdate.py +4 -6
  28. tradedangerous/edsmupdate.py +4 -4
  29. tradedangerous/formatting.py +53 -40
  30. tradedangerous/fs.py +6 -6
  31. tradedangerous/gui.py +53 -62
  32. tradedangerous/jsonprices.py +8 -16
  33. tradedangerous/mapping.py +4 -3
  34. tradedangerous/mfd/__init__.py +2 -4
  35. tradedangerous/mfd/saitek/__init__.py +0 -1
  36. tradedangerous/mfd/saitek/directoutput.py +8 -11
  37. tradedangerous/mfd/saitek/x52pro.py +5 -7
  38. tradedangerous/misc/checkpricebounds.py +2 -3
  39. tradedangerous/misc/clipboard.py +2 -3
  40. tradedangerous/misc/coord64.py +2 -1
  41. tradedangerous/misc/derp-sentinel.py +1 -1
  42. tradedangerous/misc/diff-system-csvs.py +3 -0
  43. tradedangerous/misc/eddb.py +1 -3
  44. tradedangerous/misc/eddn.py +2 -2
  45. tradedangerous/misc/edsc.py +7 -14
  46. tradedangerous/misc/edsm.py +1 -8
  47. tradedangerous/misc/importeddbstats.py +2 -1
  48. tradedangerous/misc/prices-json-exp.py +7 -5
  49. tradedangerous/misc/progress.py +2 -2
  50. tradedangerous/plugins/__init__.py +2 -2
  51. tradedangerous/plugins/edapi_plug.py +13 -19
  52. tradedangerous/plugins/edcd_plug.py +4 -5
  53. tradedangerous/plugins/eddblink_plug.py +11 -15
  54. tradedangerous/plugins/edmc_batch_plug.py +3 -5
  55. tradedangerous/plugins/journal_plug.py +2 -1
  56. tradedangerous/plugins/netlog_plug.py +5 -5
  57. tradedangerous/plugins/spansh_plug.py +394 -170
  58. tradedangerous/prices.py +19 -20
  59. tradedangerous/submit-distances.py +3 -8
  60. tradedangerous/templates/TradeDangerous.sql +305 -306
  61. tradedangerous/trade.py +12 -5
  62. tradedangerous/tradecalc.py +30 -34
  63. tradedangerous/tradedb.py +140 -206
  64. tradedangerous/tradeenv.py +143 -69
  65. tradedangerous/tradegui.py +4 -2
  66. tradedangerous/transfers.py +23 -20
  67. tradedangerous/version.py +1 -1
  68. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.0.dist-info}/METADATA +2 -2
  69. tradedangerous-11.0.0.dist-info/RECORD +79 -0
  70. tradedangerous-10.17.0.dist-info/RECORD +0 -79
  71. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.0.dist-info}/LICENSE +0 -0
  72. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.0.dist-info}/WHEEL +0 -0
  73. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.0.dist-info}/entry_points.txt +0 -0
  74. {tradedangerous-10.17.0.dist-info → tradedangerous-11.0.0.dist-info}/top_level.txt +0 -0
tradedangerous/tradedb.py CHANGED
@@ -49,41 +49,32 @@ Simplistic use might be:
49
49
 
50
50
  ######################################################################
51
51
  # Imports
52
+ from __future__ import annotations
52
53
 
53
-
54
- from collections import namedtuple, defaultdict
55
- from pathlib import Path
56
- from .tradeenv import TradeEnv
57
- from .tradeexcept import TradeException
58
-
59
- from . import cache, fs
54
+ from collections import namedtuple
60
55
  from contextlib import closing
56
+ from math import sqrt as math_sqrt
57
+ from pathlib import Path
61
58
  import heapq
62
59
  import itertools
63
60
  import locale
64
- import math
65
- import os
66
61
  import re
67
62
  import sqlite3
68
63
  import sys
64
+ import typing
65
+
66
+ from .tradeenv import TradeEnv
67
+ from .tradeexcept import TradeException
68
+ from . import cache, fs
69
+
70
+ if typing.TYPE_CHECKING:
71
+ from typing import Generator
72
+ from typing import Optional, Union
69
73
 
70
- haveNumpy = False
71
- try:
72
- import numpy
73
- import numpy.linalg
74
- haveNumpy = True
75
- except (KeyError, ImportError):
76
- pass
77
- if not haveNumpy:
78
- class numpy(object):
79
- array = False
80
- float32 = False
81
- ascontiguousarray = False
82
- class linalg(object):
83
- norm = False
84
74
 
85
75
  locale.setlocale(locale.LC_ALL, '')
86
76
 
77
+
87
78
  ######################################################################
88
79
  # Classes
89
80
 
@@ -115,22 +106,20 @@ class AmbiguityError(TradeException):
115
106
  key(c) for c in anyMatch[0:-1]
116
107
  )
117
108
  opportunities += " or " + key(anyMatch[-1])
118
- return '{} "{}" could match {}'.format(
119
- self.lookupType, str(self.searchKey),
120
- opportunities
121
- )
109
+ return f'{self.lookupType} "{self.searchKey}" could match {opportunities}'
122
110
 
123
111
  class SystemNotStationError(TradeException):
124
112
  """
125
113
  Raised when a station lookup matched a System but
126
114
  could not be automatically reduced to a Station.
127
115
  """
128
- pass
116
+ pass # pylint: disable=unnecessary-pass # (it's not)
117
+
129
118
 
130
119
  ######################################################################
131
120
 
132
121
 
133
- def makeStellarGridKey(x, y, z):
122
+ def make_stellar_grid_key(x: float, y: float, z: float) -> int:
134
123
  """
135
124
  The Stellar Grid is a map of systems based on their Stellar
136
125
  co-ordinates rounded down to 32lys. This makes it much easier
@@ -138,7 +127,8 @@ def makeStellarGridKey(x, y, z):
138
127
  """
139
128
  return (int(x) >> 5, int(y) >> 5, int(z) >> 5)
140
129
 
141
- class System(object):
130
+
131
+ class System:
142
132
  """
143
133
  Describes a star system which may contain one or more Station objects.
144
134
 
@@ -152,65 +142,28 @@ class System(object):
152
142
  '_rangeCache'
153
143
  )
154
144
 
155
- class RangeCache(object):
145
+ class RangeCache:
156
146
  """
157
147
  Lazily populated cache of neighboring systems.
158
148
  """
159
149
  def __init__(self):
160
150
  self.systems = []
161
- self.probedLy = 0.
151
+ self.probed_ly = 0.
162
152
 
163
- def __init__(
164
- self, ID, dbname, posX, posY, posZ, addedID,
165
- ary=numpy.array,
166
- nptype=numpy.float32,
167
- ):
153
+ def __init__(self, ID, dbname, posX, posY, posZ, addedID) -> None:
168
154
  self.ID = ID
169
155
  self.dbname = dbname
170
156
  self.posX, self.posY, self.posZ = posX, posY, posZ
171
- if ary:
172
- self.pos = ary([posX, posY, posZ], nptype)
173
157
  self.addedID = addedID or 0
174
158
  self.stations = ()
175
159
  self._rangeCache = None
176
160
 
177
161
  @property
178
- def system(self):
162
+ def system(self) -> 'System':
163
+ """ Returns self for compatibility with the undefined 'Positional' interface. """
179
164
  return self
180
165
 
181
- def distToSq(self, other):
182
- """
183
- Returns the square of the distance between two systems.
184
-
185
- It is slightly cheaper to calculate the square of the
186
- distance between two points, so when you are primarily
187
- doing distance checks you can use this less expensive
188
- distance query and only perform a sqrt (** 0.5) on the
189
- distances that fall within your constraint.
190
-
191
- Args:
192
- other:
193
- The other System to measure the distance between.
194
-
195
- Returns:
196
- Distance in light years to the power of 2 (i.e. squared).
197
-
198
- Example:
199
- # Calculate which of [systems] is within 12 ly
200
- # of System "target".
201
- maxLySq = 12 ** 2 # Maximum of 12 ly.
202
- inRange = []
203
- for sys in systems:
204
- if sys.distToSq(target) <= maxLySq:
205
- inRange.append(sys)
206
- """
207
- return (
208
- (self.posX - other.posX) ** 2 +
209
- (self.posY - other.posY) ** 2 +
210
- (self.posZ - other.posZ) ** 2
211
- )
212
-
213
- def distanceTo(self, other):
166
+ def distanceTo(self, other: 'System') -> float:
214
167
  """
215
168
  Returns the distance (in ly) between two systems.
216
169
 
@@ -226,26 +179,10 @@ class System(object):
226
179
  lhs.distanceTo(rhs),
227
180
  ))
228
181
  """
229
- return (
230
- (self.posX - other.posX) ** 2 +
231
- (self.posY - other.posY) ** 2 +
232
- (self.posZ - other.posZ) ** 2
233
- ) ** 0.5 # fast sqrt
234
-
235
- if haveNumpy:
236
- def all_distances(
237
- self, iterable,
238
- ary=numpy.ascontiguousarray, norm=numpy.linalg.norm,
239
- ):
240
- """
241
- Takes a list of systems and returns their distances from this system.
242
- """
243
- return numpy.linalg.norm(
244
- ary([s.pos for s in iterable]) - self.pos,
245
- ord=2, axis=1.
246
- )
182
+ dx, dy, dz = self.posX - other.posX, self.posY - other.posY, self.posZ - other.posZ
183
+ return math_sqrt(dx * dx + dy * dy + dz * dz)
247
184
 
248
- def getStation(self, stationName):
185
+ def getStation(self, name: str) -> 'Optional[Station]':
249
186
  """
250
187
  Quick case-insensitive lookup of a station name within the
251
188
  stations in this system.
@@ -254,16 +191,17 @@ class System(object):
254
191
  Station() object if a match is found,
255
192
  otherwise None.
256
193
  """
257
- upperName = stationName.upper()
194
+ name = name.upper()
258
195
  for station in self.stations:
259
- if station.dbname.upper() == upperName:
196
+ if station.name == name:
260
197
  return station
261
198
  return None
262
199
 
263
- def name(self, detail=0):
200
+ def name(self, detail: int = 0) -> str: # pylint: disable=unused-argument
201
+ """ Returns the display name for this System."""
264
202
  return self.dbname
265
203
 
266
- def str(self):
204
+ def text(self) -> str:
267
205
  return self.dbname
268
206
 
269
207
  ######################################################################
@@ -278,7 +216,7 @@ class DestinationNode(namedtuple('DestinationNode', [
278
216
  ])):
279
217
  pass
280
218
 
281
- class Station(object):
219
+ class Station:
282
220
  """
283
221
  Describes a station (trading or otherwise) in a system.
284
222
 
@@ -315,8 +253,8 @@ class Station(object):
315
253
  self.dataAge = dataAge
316
254
  system.stations = system.stations + (self,)
317
255
 
318
- def name(self, detail=0):
319
- return '%s/%s' % (self.system.dbname, self.dbname)
256
+ def name(self, detail: int = 0) -> str: # pylint: disable=unused-argument
257
+ return f"{self.system.dbname}/{self.dbname}"
320
258
 
321
259
  def checkPadSize(self, maxPadSize):
322
260
  """
@@ -379,16 +317,16 @@ class Station(object):
379
317
  Same as checkPlanetary, but for fleet carriers.
380
318
  """
381
319
  return (not fleet or self.fleet in fleet)
382
-
383
-
320
+
321
+
384
322
  def checkOdyssey(self, odyssey):
385
323
  """
386
324
  Same as checkPlanetary, but for Odyssey.
387
325
  """
388
326
  return (not odyssey or self.odyssey in odyssey)
389
-
390
-
391
- def distFromStar(self, addSuffix=False):
327
+
328
+
329
+ def distFromStar(self, addSuffix: bool = False) -> str:
392
330
  """
393
331
  Returns a textual description of the distance from this
394
332
  Station to the parent star.
@@ -399,23 +337,20 @@ class Station(object):
399
337
  """
400
338
  ls = self.lsFromStar
401
339
  if not ls:
402
- if addSuffix:
403
- return "Unk"
404
- else:
405
- return '?'
340
+ return "Unk" if addSuffix else "?"
341
+
342
+ suffix = "ls" if addSuffix else ""
343
+
406
344
  if ls < 1000:
407
- suffix = 'ls' if addSuffix else ''
408
- return '{:n}'.format(ls)+suffix
345
+ return f"{ls:n}{suffix}"
409
346
  if ls < 10000:
410
- suffix = 'ls' if addSuffix else ''
411
- return '{:.2f}K'.format(ls / 1000)+suffix
347
+ return f"{ls / 1000:.2f}K{suffix}"
412
348
  if ls < 1000000:
413
- suffix = 'ls' if addSuffix else ''
414
- return '{:n}K'.format(int(ls / 1000))+suffix
415
- return '{:.2f}ly'.format(ls / (365*24*60*60))
349
+ return f"{int(ls / 1000):n}K{suffix}"
350
+ return f'{ls / (365*24*60*60):.2f}ly'
416
351
 
417
352
  @property
418
- def isTrading(self):
353
+ def isTrading(self) -> bool:
419
354
  """
420
355
  True if the station is thought to be trading.
421
356
 
@@ -428,11 +363,11 @@ class Station(object):
428
363
  def itemDataAgeStr(self):
429
364
  """ Returns the age in days of item data if present, else "-". """
430
365
  if self.itemCount and self.dataAge:
431
- return "{:7.2f}".format(self.dataAge)
366
+ return f"{self.dataAge:7.2f}"
432
367
  return "-"
433
368
 
434
- def str(self):
435
- return '%s/%s' % (self.system.dbname, self.dbname)
369
+ def text(self) -> str:
370
+ return f"{self.system.dbname}/{self.dbname}"
436
371
 
437
372
  ######################################################################
438
373
 
@@ -450,7 +385,7 @@ class Ship(namedtuple('Ship', (
450
385
  stations -- List of Stations ship is sold at.
451
386
  """
452
387
 
453
- def name(self, detail=0):
388
+ def name(self, detail=0): # pylint: disable=unused-argument
454
389
  return self.dbname
455
390
 
456
391
  ######################################################################
@@ -478,13 +413,13 @@ class Category(namedtuple('Category', (
478
413
  Returns the display name for this Category.
479
414
  """
480
415
 
481
- def name(self, detail=0):
416
+ def name(self, detail=0): # pylint: disable=unused-argument
482
417
  return self.dbname.upper()
483
418
 
484
419
  ######################################################################
485
420
 
486
421
 
487
- class Item(object):
422
+ class Item:
488
423
  """
489
424
  A product that can be bought/sold in the game.
490
425
 
@@ -554,7 +489,7 @@ class Trade(namedtuple('Trade', (
554
489
  ######################################################################
555
490
 
556
491
 
557
- class TradeDB(object):
492
+ class TradeDB:
558
493
  """
559
494
  Encapsulation for the database layer.
560
495
 
@@ -635,7 +570,7 @@ class TradeDB(object):
635
570
  load=True,
636
571
  debug=None,
637
572
  ):
638
- self.conn : sqlite3.Connection = None
573
+ self.conn: sqlite3.Connection = None
639
574
  self.tradingCount = None
640
575
 
641
576
  tdenv = tdenv or TradeEnv(debug=(debug or 0))
@@ -666,6 +601,18 @@ class TradeDB(object):
666
601
 
667
602
  self.avgSelling, self.avgBuying = None, None
668
603
  self.tradingStationCount = 0
604
+ self.addedByID = None
605
+ self.systemByID = None
606
+ self.systemByName = None
607
+ self.stellarGrid = None
608
+ self.stationByID = None
609
+ self.shipByID = None
610
+ self.categoryByID = None
611
+ self.itemByID = None
612
+ self.itemByName = None
613
+ self.itemByFDevID = None
614
+ self.rareItemByID = None
615
+ self.rareItemByName = None
669
616
 
670
617
  if load:
671
618
  self.reloadCache()
@@ -676,22 +623,16 @@ class TradeDB(object):
676
623
  """
677
624
  Returns the distance in ly between two points.
678
625
  """
679
- dX = (lx - rx)
680
- dY = (ly - ry)
681
- dZ = (lz - rz)
682
- distSq = (dX ** 2) + (dY ** 2) + (dZ ** 2)
683
- return distSq
626
+ dx, dy, dz = lx - rx, ly - ry, lz - rz
627
+ return (dx * dx) + (dy * dy) + (dz * dz)
684
628
 
685
629
  @staticmethod
686
630
  def calculateDistance(lx, ly, lz, rx, ry, rz):
687
631
  """
688
632
  Returns the distance in ly between two points.
689
633
  """
690
- dX = (lx - rx)
691
- dY = (ly - ry)
692
- dZ = (lz - rz)
693
- distSq = (dX ** 2) + (dY ** 2) + (dZ ** 2)
694
- return distSq ** 0.5
634
+ dx, dy, dz = lx - rx, ly - ry, lz - rz
635
+ return math_sqrt((dx * dx) + (dy * dy) + (dz * dz))
695
636
 
696
637
  ############################################################
697
638
  # Access to the underlying database.
@@ -705,7 +646,7 @@ class TradeDB(object):
705
646
  conn.execute("PRAGMA synchronous=OFF")
706
647
  conn.execute("PRAGMA temp_store=MEMORY")
707
648
  conn.execute("PRAGMA auto_vacuum=INCREMENTAL")
708
-
649
+
709
650
  conn.create_function('dist2', 6, TradeDB.calculateDistance2)
710
651
  self.conn = conn
711
652
  return conn
@@ -912,7 +853,7 @@ class TradeDB(object):
912
853
  commit=True,
913
854
  ):
914
855
  """ Removes a system and it's stations from the local DB. """
915
- for stn in self.stations:
856
+ for stn in self.stations():
916
857
  self.removeLocalStation(stn, commit=False)
917
858
  db = self.getDB()
918
859
  db.execute("""
@@ -939,9 +880,9 @@ class TradeDB(object):
939
880
  Divides the galaxy into a fixed-sized grid allowing us to
940
881
  aggregate small numbers of stars by locality.
941
882
  """
942
- stellarGrid = self.stellarGrid = dict()
883
+ stellarGrid = self.stellarGrid = {}
943
884
  for system in self.systemByID.values():
944
- key = makeStellarGridKey(system.posX, system.posY, system.posZ)
885
+ key = make_stellar_grid_key(system.posX, system.posY, system.posZ)
945
886
  try:
946
887
  grid = stellarGrid[key]
947
888
  except KeyError:
@@ -970,9 +911,9 @@ class TradeDB(object):
970
911
  self.__buildStellarGrid()
971
912
 
972
913
  sysX, sysY, sysZ = system.posX, system.posY, system.posZ
973
- lwrBound = makeStellarGridKey(sysX - ly, sysY - ly, sysZ - ly)
974
- uprBound = makeStellarGridKey(sysX + ly, sysY + ly, sysZ + ly)
975
- lySq = ly ** 2
914
+ lwrBound = make_stellar_grid_key(sysX - ly, sysY - ly, sysZ - ly)
915
+ uprBound = make_stellar_grid_key(sysX + ly, sysY + ly, sysZ + ly)
916
+ lySq = ly * ly # in 64-bit python, ** invokes a function call making it 4x expensive as *.
976
917
  stellarGrid = self.stellarGrid
977
918
  for x in range(lwrBound[0], uprBound[0]+1):
978
919
  for y in range(lwrBound[1], uprBound[1]+1):
@@ -982,17 +923,20 @@ class TradeDB(object):
982
923
  except KeyError:
983
924
  continue
984
925
  for candidate in grid:
985
- distSq = (candidate.posX - sysX) ** 2
926
+ delta = candidate.posX - sysX
927
+ distSq = delta * delta
986
928
  if distSq > lySq:
987
929
  continue
988
- distSq += (candidate.posY - sysY) ** 2
930
+ delta = candidate.posY - sysY
931
+ distSq += delta * delta
989
932
  if distSq > lySq:
990
933
  continue
991
- distSq += (candidate.posZ - sysZ) ** 2
934
+ delta = candidate.posZ - sysZ
935
+ distSq += delta * delta
992
936
  if distSq > lySq:
993
937
  continue
994
938
  if candidate is not system:
995
- yield candidate, distSq ** 0.5
939
+ yield candidate, math_sqrt(distSq)
996
940
 
997
941
  def genSystemsInRange(self, system, ly, includeSelf=False):
998
942
  """
@@ -1016,32 +960,32 @@ class TradeDB(object):
1016
960
  The distance in lightyears between system and candidate.
1017
961
  """
1018
962
 
1019
- cache = system._rangeCache
1020
- if not cache:
1021
- cache = system._rangeCache = System.RangeCache()
1022
- cachedSystems = cache.systems
963
+ cur_cache = system._rangeCache # pylint: disable=protected-access
964
+ if not cur_cache:
965
+ cur_cache = system._rangeCache = System.RangeCache()
966
+ cached_systems = cur_cache.systems
1023
967
 
1024
- if ly > cache.probedLy:
968
+ if ly > cur_cache.probed_ly:
1025
969
  # Consult the database for stars we haven't seen.
1026
- cachedSystems = cache.systems = list(
970
+ cached_systems = cur_cache.systems = list(
1027
971
  self.genStellarGrid(system, ly)
1028
972
  )
1029
- cachedSystems.sort(key=lambda ent: ent[1])
1030
- cache.probedLy = ly
973
+ cached_systems.sort(key=lambda ent: ent[1])
974
+ cur_cache.probed_ly = ly
1031
975
 
1032
976
  if includeSelf:
1033
977
  yield system, 0.
1034
978
 
1035
- if cache.probedLy > ly:
979
+ if cur_cache.probed_ly > ly:
1036
980
  # Cache may contain values outside our view
1037
- for sys, dist in cachedSystems:
981
+ for candidate, dist in cached_systems:
1038
982
  if dist <= ly:
1039
- yield sys, dist
983
+ yield candidate, dist
1040
984
  else:
1041
985
  # No need to be conditional inside the loop
1042
- yield from cachedSystems
986
+ yield from cached_systems
1043
987
 
1044
- def getRoute(self, origin, dest, maxJumpLy, avoiding=[], stationInterval=0):
988
+ def getRoute(self, origin, dest, maxJumpLy, avoiding=None, stationInterval=0):
1045
989
  """
1046
990
  Find a shortest route between two systems with an additional
1047
991
  constraint that each system be a maximum of maxJumpLy from
@@ -1083,6 +1027,9 @@ class TradeDB(object):
1083
1027
 
1084
1028
  """
1085
1029
 
1030
+ if avoiding is None:
1031
+ avoiding = []
1032
+
1086
1033
  if isinstance(origin, Station):
1087
1034
  origin = origin.system
1088
1035
  if isinstance(dest, Station):
@@ -1119,12 +1066,11 @@ class TradeDB(object):
1119
1066
 
1120
1067
  maxPadSize = self.tdenv.padSize
1121
1068
  if not maxPadSize:
1122
- checkStations = lambda system: bool(system.stations())
1069
+ def checkStations(system: System) -> bool: # pylint: disable=function-redefined, missing-docstring
1070
+ return bool(system.stations())
1123
1071
  else:
1124
- checkStations = lambda system: any(
1125
- stn for stn in system.stations
1126
- if stn.checkPadSize(maxPadSize)
1127
- )
1072
+ def checkStations(system: System) -> bool: # pylint: disable=function-redefined, missing-docstring
1073
+ return any(stn for stn in system.stations if stn.checkPadSize(maxPadSize))
1128
1074
 
1129
1075
  while openSet:
1130
1076
  weight, curDist, curSysID, stnDist = heappop(openSet)
@@ -1182,7 +1128,7 @@ class TradeDB(object):
1182
1128
  ############################################################
1183
1129
  # Station data.
1184
1130
 
1185
- def stations(self):
1131
+ def stations(self) -> 'Generator[Station, None, None]':
1186
1132
  """ Iterate through the list of stations. """
1187
1133
  yield from self.stationByID.values()
1188
1134
 
@@ -1211,7 +1157,7 @@ class TradeDB(object):
1211
1157
  ID, systemID, name,
1212
1158
  lsFromStar, market, blackMarket, shipyard,
1213
1159
  maxPadSize, outfitting, rearm, refuel, repair, planetary, type_id
1214
- ) in cur :
1160
+ ) in cur:
1215
1161
  isFleet = 'Y' if int(type_id) in types['fleet-carrier'] else 'N'
1216
1162
  isOdyssey = 'Y' if int(type_id) in types['odyssey'] else 'N'
1217
1163
  station = Station(
@@ -1372,7 +1318,7 @@ class TradeDB(object):
1372
1318
 
1373
1319
  def _changed(label, old, new):
1374
1320
  changes.append(
1375
- "{}('{}'=>'{}')".format(label, old, new)
1321
+ f"{label}('{old}'=>'{new}')"
1376
1322
  )
1377
1323
 
1378
1324
  if name is not None:
@@ -1510,7 +1456,7 @@ class TradeDB(object):
1510
1456
  @system/station
1511
1457
  """
1512
1458
 
1513
- if isinstance(name, System) or isinstance(name, Station):
1459
+ if isinstance(name, (System, Station)):
1514
1460
  return name
1515
1461
 
1516
1462
  slashPos = name.find('/')
@@ -1577,7 +1523,8 @@ class TradeDB(object):
1577
1523
  else:
1578
1524
  anyMatch.append(place)
1579
1525
  continue
1580
- elif subPos > 0:
1526
+
1527
+ if subPos > 0:
1581
1528
  if placeNameNorm[subPos] == ' ' and \
1582
1529
  placeNameNorm[subPos + nameNormLen] == ' ':
1583
1530
  wordMatch.append(place)
@@ -1602,10 +1549,11 @@ class TradeDB(object):
1602
1549
 
1603
1550
  if sysName:
1604
1551
  try:
1605
- sys = self.systemByName[sysName]
1606
- exactMatch = [sys]
1552
+ system = self.systemByName[sysName]
1553
+ exactMatch = [system]
1607
1554
  except KeyError:
1608
1555
  lookup(sysName, self.systemByID.values())
1556
+
1609
1557
  if stnName:
1610
1558
  # Are we considering the name as a station?
1611
1559
  # (we don't if they type, e,g '@aulin')
@@ -1616,10 +1564,10 @@ class TradeDB(object):
1616
1564
  # stations we compare against. Check first if there are
1617
1565
  # any matches.
1618
1566
  stationCandidates = []
1619
- for sys in itertools.chain(
1567
+ for system in itertools.chain(
1620
1568
  exactMatch, closeMatch, wordMatch, anyMatch
1621
1569
  ):
1622
- stationCandidates += sys.stations
1570
+ stationCandidates += system.stations
1623
1571
  # Clear out the candidate lists
1624
1572
  exactMatch = []
1625
1573
  closeMatch = []
@@ -1643,7 +1591,7 @@ class TradeDB(object):
1643
1591
  # Note: this was a TradeException and may need to be again,
1644
1592
  # but then we need to catch that error in commandenv
1645
1593
  # when we process avoids
1646
- raise LookupError("Unrecognized place: {}".format(name))
1594
+ raise LookupError(f"Unrecognized place: {name}")
1647
1595
 
1648
1596
  # More than one match
1649
1597
  raise AmbiguityError(
@@ -1661,12 +1609,7 @@ class TradeDB(object):
1661
1609
  if isinstance(name, System):
1662
1610
  # When given a system with only one station, return the station.
1663
1611
  if len(name.stations) != 1:
1664
- raise SystemNotStationError(
1665
- "System '%s' has %d stations, "
1666
- "please specify a station instead." % (
1667
- name.str(), len(name.stations)
1668
- )
1669
- )
1612
+ raise SystemNotStationError(f"System '{name}' has {len(name.stations)} stations, please specify a station instead.")
1670
1613
  return name.stations[0]
1671
1614
 
1672
1615
  if system:
@@ -1675,7 +1618,7 @@ class TradeDB(object):
1675
1618
  "Station", name, system.stations,
1676
1619
  key=lambda system: system.dbname)
1677
1620
 
1678
- stationID, station, systemID, system = None, None, None, None
1621
+ station, system = None, None
1679
1622
  try:
1680
1623
  system = TradeDB.listSearch(
1681
1624
  "System", name, self.systemByID.values(),
@@ -1692,9 +1635,7 @@ class TradeDB(object):
1692
1635
  pass
1693
1636
  # If neither matched, we have a lookup error.
1694
1637
  if not (station or system):
1695
- raise LookupError(
1696
- "'%s' did not match any station or system." % (name)
1697
- )
1638
+ raise LookupError(f"'{name}' did not match any station or system.")
1698
1639
 
1699
1640
  # If we matched both a station and a system, make sure they resovle to
1700
1641
  # the same station otherwise we have an ambiguity. Some stations have
@@ -1711,10 +1652,7 @@ class TradeDB(object):
1711
1652
  # system otherwise they need to specify a station name.
1712
1653
  if len(system.stations) != 1:
1713
1654
  raise SystemNotStationError(
1714
- "System '%s' has %d stations, "
1715
- "please specify a station instead." % (
1716
- system.name(), len(system.stations)
1717
- )
1655
+ f"System '{system.name()}' has {len(system.stations)} stations, please specify a station instead."
1718
1656
  )
1719
1657
  return system.stations[0]
1720
1658
 
@@ -1911,7 +1849,7 @@ class TradeDB(object):
1911
1849
  category = self.categoryByID[categoryID]
1912
1850
  item = Item(
1913
1851
  ID, name, category,
1914
- '{}/{}'.format(category.dbname, name),
1852
+ f"{category.dbname}/{name}",
1915
1853
  avgPrice, fdevID
1916
1854
  )
1917
1855
  itemByID[ID] = item
@@ -1945,7 +1883,7 @@ class TradeDB(object):
1945
1883
  Query the database for average selling prices of all items.
1946
1884
  """
1947
1885
  if not self.avgSelling:
1948
- self.avgSelling = {itemID: 0 for itemID in self.itemByID.keys()}
1886
+ self.avgSelling = {itemID: 0 for itemID in self.itemByID}
1949
1887
  self.avgSelling.update({
1950
1888
  ID: int(cr)
1951
1889
  for ID, cr in self.getDB().execute("""
@@ -1965,7 +1903,7 @@ class TradeDB(object):
1965
1903
  Query the database for average buying prices of all items.
1966
1904
  """
1967
1905
  if not self.avgBuying:
1968
- self.avgBuying = {itemID: 0 for itemID in self.itemByID.keys()}
1906
+ self.avgBuying = {itemID: 0 for itemID in self.itemByID}
1969
1907
  self.avgBuying.update({
1970
1908
  ID: int(cr)
1971
1909
  for ID, cr in self.getDB().execute("""
@@ -1992,8 +1930,8 @@ class TradeDB(object):
1992
1930
  cost, max_allocation, illegal, suppressed
1993
1931
  FROM RareItem
1994
1932
  """
1995
-
1996
-
1933
+
1934
+
1997
1935
  rareItemByID, rareItemByName = {}, {}
1998
1936
  stationByID = self.stationByID
1999
1937
  with closing(self.query(stmt)) as cur:
@@ -2005,7 +1943,7 @@ class TradeDB(object):
2005
1943
  category = self.categoryByID[catID]
2006
1944
  rare = RareItem(
2007
1945
  ID, station, name, cost, maxAlloc, illegal, suppressed,
2008
- category, '{}/{}'.format(category.dbname, name)
1946
+ category, f"{category.dbname}/{name}"
2009
1947
  )
2010
1948
  rareItemByID[ID] = rareItemByName[name] = rare
2011
1949
  self.rareItemByID = rareItemByID
@@ -2020,7 +1958,6 @@ class TradeDB(object):
2020
1958
  # Price data.
2021
1959
 
2022
1960
  def close(self):
2023
- self.cur = None
2024
1961
  if self.conn:
2025
1962
  self.conn.close()
2026
1963
  self.conn = None
@@ -2036,8 +1973,8 @@ class TradeDB(object):
2036
1973
  """
2037
1974
 
2038
1975
  self.tdenv.DEBUG1("Loading data")
2039
-
2040
-
1976
+
1977
+
2041
1978
 
2042
1979
  self._loadAdded()
2043
1980
  self._loadSystems()
@@ -2082,7 +2019,7 @@ class TradeDB(object):
2082
2019
  needle = lookup.translate(normTrans).translate(trimTrans)
2083
2020
  partialMatch, wordMatch = [], []
2084
2021
  # make a regex to match whole words
2085
- wordRe = re.compile(r'\b{}\b'.format(lookup), re.IGNORECASE)
2022
+ wordRe = re.compile(f"\\b{lookup}\\b", re.IGNORECASE)
2086
2023
  # describe a match
2087
2024
  for entry in values:
2088
2025
  entryKey = key(entry)
@@ -2113,12 +2050,10 @@ class TradeDB(object):
2113
2050
  )
2114
2051
  return partialMatch[0].value
2115
2052
  # No matches
2116
- raise LookupError(
2117
- "Error: '%s' doesn't match any %s" % (lookup, listType)
2118
- )
2053
+ raise LookupError(f"Error: '{lookup}' doesn't match any {listType}")
2119
2054
 
2120
2055
  @staticmethod
2121
- def normalizedStr(text):
2056
+ def normalizedStr(text: str) -> str:
2122
2057
  """
2123
2058
  Returns a case folded, sanitized version of 'str' suitable for
2124
2059
  performing simple and partial matches against. Removes various
@@ -2134,7 +2069,7 @@ class TradeDB(object):
2134
2069
  ######################################################################
2135
2070
  # Assorted helpers
2136
2071
 
2137
- def describeAge(ageInSeconds):
2072
+ def describeAge(ageInSeconds: Union[float, int]) -> str:
2138
2073
  """
2139
2074
  Turns an age (in seconds) into a text representation.
2140
2075
  """
@@ -2144,10 +2079,9 @@ def describeAge(ageInSeconds):
2144
2079
  if hours == 1:
2145
2080
  return "1 hr"
2146
2081
  if hours < 48:
2147
- return str(hours) + " hrs"
2082
+ return f"{hours} hrs"
2148
2083
  days = int(hours / 24)
2149
2084
  if days < 90:
2150
- return str(days) + " days"
2085
+ return f"{days} days"
2151
2086
 
2152
- months = int(days / 31)
2153
- return str(months) + " mths"
2087
+ return f"{int(days / 31)} mths"