1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-17 17:42:20 +03:00

Running EDMarketConnector.py 'works'.

First pass utilising 'futurize' to do most of the work.

  There's an issue with ur'\"' in l10n.py which I'm not sure how to
properly fix.

  This now has errors when hitting the 'Update' button.
This commit is contained in:
Athanasius 2019-09-10 17:14:49 +01:00
parent a8631e5bb8
commit d0ae621387
13 changed files with 145 additions and 110 deletions

View File

@ -1,6 +1,11 @@
#!/usr/bin/env python2 #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import print_function
from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import object
import sys import sys
from sys import platform from sys import platform
from collections import OrderedDict from collections import OrderedDict
@ -26,11 +31,11 @@ if getattr(sys, 'frozen', False):
if 'TCL_LIBRARY' in environ: if 'TCL_LIBRARY' in environ:
environ.pop('TCL_LIBRARY') environ.pop('TCL_LIBRARY')
import Tkinter as tk import tkinter as tk
import ttk import tkinter.ttk
import tkFileDialog import tkinter.filedialog
import tkFont import tkinter.font
import tkMessageBox import tkinter.messagebox
from ttkHyperlinkLabel import HyperlinkLabel from ttkHyperlinkLabel import HyperlinkLabel
if __debug__: if __debug__:
@ -58,7 +63,7 @@ from theme import theme
SERVER_RETRY = 5 # retry pause for Companion servers [s] SERVER_RETRY = 5 # retry pause for Companion servers [s]
class AppWindow: class AppWindow(object):
# Tkinter Event types # Tkinter Event types
EVENT_KEYPRESS = 2 EVENT_KEYPRESS = 2
@ -125,7 +130,7 @@ class AppWindow:
else: else:
appitem.grid(columnspan=2, sticky=tk.EW) appitem.grid(columnspan=2, sticky=tk.EW)
self.button = ttk.Button(frame, text=_('Update'), width=28, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window self.button = tkinter.ttk.Button(frame, text=_('Update'), width=28, default=tk.ACTIVE, state=tk.DISABLED) # Update button in main window
self.theme_button = tk.Label(frame, width = platform == 'darwin' and 32 or 28, state=tk.DISABLED) self.theme_button = tk.Label(frame, width = platform == 'darwin' and 32 or 28, state=tk.DISABLED)
self.status = tk.Label(frame, name='status', anchor=tk.W) self.status = tk.Label(frame, name='status', anchor=tk.W)
@ -377,10 +382,10 @@ class AppWindow:
self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status
self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data
except (companion.CredentialsError, companion.ServerError, companion.ServerLagging) as e: except (companion.CredentialsError, companion.ServerError, companion.ServerLagging) as e:
self.status['text'] = unicode(e) self.status['text'] = str(e)
except Exception as e: except Exception as e:
if __debug__: print_exc() if __debug__: print_exc()
self.status['text'] = unicode(e) self.status['text'] = str(e)
self.cooldown() self.cooldown()
def getandsend(self, event=None, retrying=False): def getandsend(self, event=None, retrying=False):
@ -479,7 +484,7 @@ class AppWindow:
# Companion API problem # Companion API problem
except companion.ServerLagging as e: except companion.ServerLagging as e:
if retrying: if retrying:
self.status['text'] = unicode(e) self.status['text'] = str(e)
play_bad = True play_bad = True
else: else:
# Retry once if Companion server is unresponsive # Retry once if Companion server is unresponsive
@ -487,14 +492,14 @@ class AppWindow:
return # early exit to avoid starting cooldown count return # early exit to avoid starting cooldown count
except companion.CmdrError as e: # Companion API return doesn't match Journal except companion.CmdrError as e: # Companion API return doesn't match Journal
self.status['text'] = unicode(e) self.status['text'] = str(e)
play_bad = True play_bad = True
companion.session.invalidate() companion.session.invalidate()
self.login() self.login()
except Exception as e: # Including CredentialsError, ServerError except Exception as e: # Including CredentialsError, ServerError
if __debug__: print_exc() if __debug__: print_exc()
self.status['text'] = unicode(e) self.status['text'] = str(e)
play_bad = True play_bad = True
if not self.status['text']: # no errors if not self.status['text']: # no errors
@ -509,7 +514,7 @@ class AppWindow:
try: try:
data = companion.session.station() data = companion.session.station()
if __debug__: if __debug__:
print 'Retry for shipyard - ' + (data['commander'].get('docked') and (data.get('lastStarport', {}).get('ships') and 'Success' or 'Failure') or 'Undocked!') print('Retry for shipyard - ' + (data['commander'].get('docked') and (data.get('lastStarport', {}).get('ships') and 'Success' or 'Failure') or 'Undocked!'))
if not data['commander'].get('docked'): if not data['commander'].get('docked'):
pass # might have undocked while we were waiting for retry in which case station data is unreliable pass # might have undocked while we were waiting for retry in which case station data is unreliable
elif (data.get('lastSystem', {}).get('name') == monitor.system and elif (data.get('lastSystem', {}).get('name') == monitor.system and
@ -576,7 +581,7 @@ class AppWindow:
if entry['event'] in ['StartUp', 'LoadGame'] and monitor.started: if entry['event'] in ['StartUp', 'LoadGame'] and monitor.started:
# Can start dashboard monitoring # Can start dashboard monitoring
if not dashboard.start(self.w, monitor.started): if not dashboard.start(self.w, monitor.started):
print "Can't start Status monitoring" print("Can't start Status monitoring")
# Export loadout # Export loadout
if entry['event'] == 'Loadout' and not monitor.state['Captain'] and config.getint('output') & config.OUT_SHIP: if entry['event'] == 'Loadout' and not monitor.state['Captain'] and config.getint('output') & config.OUT_SHIP:
@ -605,10 +610,10 @@ class AppWindow:
self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status self.file_menu.entryconfigure(0, state=tk.NORMAL) # Status
self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data self.file_menu.entryconfigure(1, state=tk.NORMAL) # Save Raw Data
except companion.ServerError as e: except companion.ServerError as e:
self.status['text'] = unicode(e) self.status['text'] = str(e)
except Exception as e: except Exception as e:
if __debug__: print_exc() if __debug__: print_exc()
self.status['text'] = unicode(e) self.status['text'] = str(e)
self.cooldown() self.cooldown()
# Handle Status event # Handle Status event
@ -676,7 +681,7 @@ class AppWindow:
try: try:
data = companion.session.station() data = companion.session.station()
self.status['text'] = '' self.status['text'] = ''
f = tkFileDialog.asksaveasfilename(parent = self.w, f = tkinter.filedialog.asksaveasfilename(parent = self.w,
defaultextension = platform=='darwin' and '.json' or '', defaultextension = platform=='darwin' and '.json' or '',
filetypes = [('JSON', '.json'), ('All Files', '*')], filetypes = [('JSON', '.json'), ('All Files', '*')],
initialdir = config.get('outdir'), initialdir = config.get('outdir'),
@ -688,7 +693,7 @@ class AppWindow:
self.status['text'] = str(e) self.status['text'] = str(e)
except Exception as e: except Exception as e:
if __debug__: print_exc() if __debug__: print_exc()
self.status['text'] = unicode(e) self.status['text'] = str(e)
def onexit(self, event=None): def onexit(self, event=None):
if platform!='darwin' or self.w.winfo_rooty()>0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7 if platform!='darwin' or self.w.winfo_rooty()>0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
@ -796,7 +801,7 @@ if __name__ == "__main__":
# By default py2exe tries to write log to dirname(sys.executable) which fails when installed # By default py2exe tries to write log to dirname(sys.executable) which fails when installed
import tempfile import tempfile
sys.stdout = sys.stderr = open(join(tempfile.gettempdir(), '%s.log' % appname), 'wt', 0) # unbuffered sys.stdout = sys.stderr = open(join(tempfile.gettempdir(), '%s.log' % appname), 'wt', 0) # unbuffered
print '%s %s %s' % (applongname, appversion, strftime('%Y-%m-%dT%H:%M:%S', localtime())) print('%s %s %s' % (applongname, appversion, strftime('%Y-%m-%dT%H:%M:%S', localtime())))
Translations.install(config.get('language') or None) # Can generate errors so wait til log set up Translations.install(config.get('language') or None) # Can generate errors so wait til log set up

View File

@ -1,7 +1,13 @@
from __future__ import print_function
from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import range
from builtins import object
import base64 import base64
import csv import csv
import requests import requests
from cookielib import LWPCookieJar # No longer needed but retained in case plugins use it from http.cookiejar import LWPCookieJar # No longer needed but retained in case plugins use it
from email.utils import parsedate from email.utils import parsedate
import hashlib import hashlib
import json import json
@ -11,7 +17,7 @@ from os.path import dirname, isfile, join
import sys import sys
import time import time
from traceback import print_exc from traceback import print_exc
import urlparse import urllib.parse
import webbrowser import webbrowser
import zlib import zlib
@ -104,7 +110,7 @@ def listify(thing):
return list(thing) # array is not sparse return list(thing) # array is not sparse
elif isinstance(thing, dict): elif isinstance(thing, dict):
retval = [] retval = []
for k,v in thing.iteritems(): for k,v in thing.items():
idx = int(k) idx = int(k)
if idx >= len(retval): if idx >= len(retval):
retval.extend([None] * (idx - len(retval))) retval.extend([None] * (idx - len(retval)))
@ -121,36 +127,36 @@ class ServerError(Exception):
def __unicode__(self): def __unicode__(self):
return _('Error: Frontier server is down') # Raised when cannot contact the Companion API server return _('Error: Frontier server is down') # Raised when cannot contact the Companion API server
def __str__(self): def __str__(self):
return unicode(self).encode('utf-8') return str(self).encode('utf-8')
class ServerLagging(Exception): class ServerLagging(Exception):
def __unicode__(self): def __unicode__(self):
return _('Error: Frontier server is lagging') # Raised when Companion API server is returning old data, e.g. when the servers are too busy return _('Error: Frontier server is lagging') # Raised when Companion API server is returning old data, e.g. when the servers are too busy
def __str__(self): def __str__(self):
return unicode(self).encode('utf-8') return str(self).encode('utf-8')
class SKUError(Exception): class SKUError(Exception):
def __unicode__(self): def __unicode__(self):
return _('Error: Frontier server SKU problem') # Raised when the Companion API server thinks that the user has not purchased E:D. i.e. doesn't have the correct 'SKU' return _('Error: Frontier server SKU problem') # Raised when the Companion API server thinks that the user has not purchased E:D. i.e. doesn't have the correct 'SKU'
def __str__(self): def __str__(self):
return unicode(self).encode('utf-8') return str(self).encode('utf-8')
class CredentialsError(Exception): class CredentialsError(Exception):
def __init__(self, message=None): def __init__(self, message=None):
self.message = message and unicode(message) or _('Error: Invalid Credentials') self.message = message and str(message) or _('Error: Invalid Credentials')
def __unicode__(self): def __unicode__(self):
return self.message return self.message
def __str__(self): def __str__(self):
return unicode(self).encode('utf-8') return str(self).encode('utf-8')
class CmdrError(Exception): class CmdrError(Exception):
def __unicode__(self): def __unicode__(self):
return _('Error: Wrong Cmdr') # Raised when the user has multiple accounts and the username/password setting is not for the account they're currently playing OR the user has reset their Cmdr and the Companion API server is still returning data for the old Cmdr return _('Error: Wrong Cmdr') # Raised when the user has multiple accounts and the username/password setting is not for the account they're currently playing OR the user has reset their Cmdr and the Companion API server is still returning data for the old Cmdr
def __str__(self): def __str__(self):
return unicode(self).encode('utf-8') return str(self).encode('utf-8')
class Auth: class Auth(object):
def __init__(self, cmdr): def __init__(self, cmdr):
self.cmdr = cmdr self.cmdr = cmdr
@ -180,16 +186,16 @@ class Auth:
config.save() # Save settings now for use by command-line app config.save() # Save settings now for use by command-line app
return data.get('access_token') return data.get('access_token')
else: else:
print 'Auth\tCan\'t refresh token for %s' % self.cmdr.encode('utf-8') print('Auth\tCan\'t refresh token for %s' % self.cmdr.encode('utf-8'))
self.dump(r) self.dump(r)
except: except:
print 'Auth\tCan\'t refresh token for %s' % self.cmdr.encode('utf-8') print('Auth\tCan\'t refresh token for %s' % self.cmdr.encode('utf-8'))
print_exc() print_exc()
else: else:
print 'Auth\tNo token for %s' % self.cmdr.encode('utf-8') print('Auth\tNo token for %s' % self.cmdr.encode('utf-8'))
# New request # New request
print 'Auth\tNew authorization request' print('Auth\tNew authorization request')
self.verifier = self.base64URLEncode(os.urandom(32)) self.verifier = self.base64URLEncode(os.urandom(32))
self.state = self.base64URLEncode(os.urandom(8)) self.state = self.base64URLEncode(os.urandom(8))
# Won't work under IE: https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/ # Won't work under IE: https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/
@ -198,16 +204,16 @@ class Auth:
def authorize(self, payload): def authorize(self, payload):
# Handle OAuth authorization code callback. Returns access token if successful, otherwise raises CredentialsError # Handle OAuth authorization code callback. Returns access token if successful, otherwise raises CredentialsError
if not '?' in payload: if not '?' in payload:
print 'Auth\tMalformed response "%s"' % payload.encode('utf-8') print('Auth\tMalformed response "%s"' % payload.encode('utf-8'))
raise CredentialsError() # Not well formed raise CredentialsError() # Not well formed
data = urlparse.parse_qs(payload[payload.index('?')+1:]) data = urllib.parse.parse_qs(payload[payload.index('?')+1:])
if not self.state or not data.get('state') or data['state'][0] != self.state: if not self.state or not data.get('state') or data['state'][0] != self.state:
print 'Auth\tUnexpected response "%s"' % payload.encode('utf-8') print('Auth\tUnexpected response "%s"' % payload.encode('utf-8'))
raise CredentialsError() # Unexpected reply raise CredentialsError() # Unexpected reply
if not data.get('code'): if not data.get('code'):
print 'Auth\tNegative response "%s"' % payload.encode('utf-8') print('Auth\tNegative response "%s"' % payload.encode('utf-8'))
if data.get('error_description'): if data.get('error_description'):
raise CredentialsError('Error: %s' % data['error_description'][0]) raise CredentialsError('Error: %s' % data['error_description'][0])
elif data.get('error'): elif data.get('error'):
@ -229,7 +235,7 @@ class Auth:
r = self.session.post(SERVER_AUTH + URL_TOKEN, data=data, timeout=auth_timeout) r = self.session.post(SERVER_AUTH + URL_TOKEN, data=data, timeout=auth_timeout)
data = r.json() data = r.json()
if r.status_code == requests.codes.ok: if r.status_code == requests.codes.ok:
print 'Auth\tNew token for %s' % self.cmdr.encode('utf-8') print('Auth\tNew token for %s' % self.cmdr.encode('utf-8'))
cmdrs = config.get('cmdrs') cmdrs = config.get('cmdrs')
idx = cmdrs.index(self.cmdr) idx = cmdrs.index(self.cmdr)
tokens = config.get('fdev_apikeys') or [] tokens = config.get('fdev_apikeys') or []
@ -239,12 +245,12 @@ class Auth:
config.save() # Save settings now for use by command-line app config.save() # Save settings now for use by command-line app
return data.get('access_token') return data.get('access_token')
except: except:
print 'Auth\tCan\'t get token for %s' % self.cmdr.encode('utf-8') print('Auth\tCan\'t get token for %s' % self.cmdr.encode('utf-8'))
print_exc() print_exc()
if r: self.dump(r) if r: self.dump(r)
raise CredentialsError() raise CredentialsError()
print 'Auth\tCan\'t get token for %s' % self.cmdr.encode('utf-8') print('Auth\tCan\'t get token for %s' % self.cmdr.encode('utf-8'))
self.dump(r) self.dump(r)
if data.get('error_description'): if data.get('error_description'):
raise CredentialsError('Error: %s' % data['error_description']) raise CredentialsError('Error: %s' % data['error_description'])
@ -257,7 +263,7 @@ class Auth:
@staticmethod @staticmethod
def invalidate(cmdr): def invalidate(cmdr):
print 'Auth\tInvalidated token for %s' % cmdr.encode('utf-8') print('Auth\tInvalidated token for %s' % cmdr.encode('utf-8'))
cmdrs = config.get('cmdrs') cmdrs = config.get('cmdrs')
idx = cmdrs.index(cmdr) idx = cmdrs.index(cmdr)
tokens = config.get('fdev_apikeys') or [] tokens = config.get('fdev_apikeys') or []
@ -267,15 +273,15 @@ class Auth:
config.save() # Save settings now for use by command-line app config.save() # Save settings now for use by command-line app
def dump(self, r): def dump(self, r):
print 'Auth\t' + r.url, r.status_code, r.reason and r.reason.decode('utf-8') or 'None', r.text.encode('utf-8') print('Auth\t' + r.url, r.status_code, r.reason and r.reason.decode('utf-8') or 'None', r.text.encode('utf-8'))
def base64URLEncode(self, text): def base64URLEncode(self, text):
return base64.urlsafe_b64encode(text).replace('=', '') return base64.urlsafe_b64encode(text).replace('=', '')
class Session: class Session(object):
STATE_INIT, STATE_AUTH, STATE_OK = range(3) STATE_INIT, STATE_AUTH, STATE_OK = list(range(3))
def __init__(self): def __init__(self):
self.state = Session.STATE_INIT self.state = Session.STATE_INIT
@ -429,7 +435,7 @@ class Session:
Auth.invalidate(self.credentials['cmdr']) Auth.invalidate(self.credentials['cmdr'])
def dump(self, r): def dump(self, r):
print 'cAPI\t' + r.url, r.status_code, r.reason and r.reason.encode('utf-8') or 'None', r.text.encode('utf-8') print('cAPI\t' + r.url, r.status_code, r.reason and r.reason.encode('utf-8') or 'None', r.text.encode('utf-8'))
# Returns a shallow copy of the received data suitable for export to older tools - English commodity names and anomalies fixed up # Returns a shallow copy of the received data suitable for export to older tools - English commodity names and anomalies fixed up
@ -451,7 +457,7 @@ def fixup(data):
# But also see https://github.com/Marginal/EDMarketConnector/issues/32 # But also see https://github.com/Marginal/EDMarketConnector/issues/32
for thing in ['buyPrice', 'sellPrice', 'demand', 'demandBracket', 'stock', 'stockBracket']: for thing in ['buyPrice', 'sellPrice', 'demand', 'demandBracket', 'stock', 'stockBracket']:
if not isinstance(commodity.get(thing), numbers.Number): if not isinstance(commodity.get(thing), numbers.Number):
if __debug__: print 'Invalid "%s":"%s" (%s) for "%s"' % (thing, commodity.get(thing), type(commodity.get(thing)), commodity.get('name', '')) if __debug__: print('Invalid "%s":"%s" (%s) for "%s"' % (thing, commodity.get(thing), type(commodity.get(thing)), commodity.get('name', '')))
break break
else: else:
if not category_map.get(commodity['categoryname'], True): # Check not marketable i.e. Limpets if not category_map.get(commodity['categoryname'], True): # Check not marketable i.e. Limpets
@ -461,13 +467,13 @@ def fixup(data):
elif commodity.get('legality'): # Check not prohibited elif commodity.get('legality'): # Check not prohibited
pass pass
elif not commodity.get('categoryname'): elif not commodity.get('categoryname'):
if __debug__: print 'Missing "categoryname" for "%s"' % commodity.get('name', '') if __debug__: print('Missing "categoryname" for "%s"' % commodity.get('name', ''))
elif not commodity.get('name'): elif not commodity.get('name'):
if __debug__: print 'Missing "name" for a commodity in "%s"' % commodity.get('categoryname', '') if __debug__: print('Missing "name" for a commodity in "%s"' % commodity.get('categoryname', ''))
elif not commodity['demandBracket'] in range(4): elif not commodity['demandBracket'] in range(4):
if __debug__: print 'Invalid "demandBracket":"%s" for "%s"' % (commodity['demandBracket'], commodity['name']) if __debug__: print('Invalid "demandBracket":"%s" for "%s"' % (commodity['demandBracket'], commodity['name']))
elif not commodity['stockBracket'] in range(4): elif not commodity['stockBracket'] in range(4):
if __debug__: print 'Invalid "stockBracket":"%s" for "%s"' % (commodity['stockBracket'], commodity['name']) if __debug__: print('Invalid "stockBracket":"%s" for "%s"' % (commodity['stockBracket'], commodity['name']))
else: else:
# Rewrite text fields # Rewrite text fields
new = dict(commodity) # shallow copy new = dict(commodity) # shallow copy
@ -498,7 +504,7 @@ def ship(data):
def filter_ship(d): def filter_ship(d):
filtered = {} filtered = {}
for k, v in d.iteritems(): for k, v in d.items():
if v == []: if v == []:
pass # just skip empty fields for brevity pass # just skip empty fields for brevity
elif k in ['alive', 'cargo', 'cockpitBreached', 'health', 'oxygenRemaining', 'rebuilds', 'starsystem', 'station']: elif k in ['alive', 'cargo', 'cockpitBreached', 'health', 'oxygenRemaining', 'rebuilds', 'starsystem', 'station']:
@ -519,7 +525,7 @@ def ship(data):
# Ship name suitable for writing to a file # Ship name suitable for writing to a file
def ship_file_name(ship_name, ship_type): def ship_file_name(ship_name, ship_type):
name = unicode(ship_name or ship_map.get(ship_type.lower(), ship_type)).strip() name = str(ship_name or ship_map.get(ship_type.lower(), ship_type)).strip()
if name.endswith('.'): if name.endswith('.'):
name = name[:-1] name = name[:-1]
if name.lower() in ['con', 'prn', 'aux', 'nul', if name.lower() in ['con', 'prn', 'aux', 'nul',

View File

@ -1,3 +1,8 @@
from __future__ import division
from builtins import str
from past.builtins import basestring
from builtins import object
from past.utils import old_div
import numbers import numbers
import sys import sys
from os import getenv, makedirs, mkdir, pardir from os import getenv, makedirs, mkdir, pardir
@ -89,7 +94,7 @@ elif platform=='linux2':
from iniparse import RawConfigParser from iniparse import RawConfigParser
class Config: class Config(object):
OUT_MKT_EDDN = 1 OUT_MKT_EDDN = 1
# OUT_MKT_BPC = 2 # No longer supported # OUT_MKT_BPC = 2 # No longer supported
@ -146,7 +151,7 @@ class Config:
elif hasattr(val, '__iter__'): elif hasattr(val, '__iter__'):
return list(val) # make writeable return list(val) # make writeable
else: else:
return unicode(val) return str(val)
def getint(self, key): def getint(self, key):
try: try:
@ -212,7 +217,7 @@ class Config:
if disposition.value == REG_CREATED_NEW_KEY: if disposition.value == REG_CREATED_NEW_KEY:
buf = ctypes.create_unicode_buffer('1') buf = ctypes.create_unicode_buffer('1')
RegSetValueEx(sparklekey, 'CheckForUpdates', 0, 1, buf, len(buf)*2) RegSetValueEx(sparklekey, 'CheckForUpdates', 0, 1, buf, len(buf)*2)
buf = ctypes.create_unicode_buffer(unicode(update_interval)) buf = ctypes.create_unicode_buffer(str(update_interval))
RegSetValueEx(sparklekey, 'UpdateInterval', 0, 1, buf, len(buf)*2) RegSetValueEx(sparklekey, 'UpdateInterval', 0, 1, buf, len(buf)*2)
RegCloseKey(sparklekey) RegCloseKey(sparklekey)
@ -224,13 +229,13 @@ class Config:
size = DWORD() size = DWORD()
if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), None, ctypes.byref(size)) or typ.value not in [REG_SZ, REG_MULTI_SZ]: if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), None, ctypes.byref(size)) or typ.value not in [REG_SZ, REG_MULTI_SZ]:
return None return None
buf = ctypes.create_unicode_buffer(size.value / 2) buf = ctypes.create_unicode_buffer(old_div(size.value, 2))
if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), buf, ctypes.byref(size)): if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), buf, ctypes.byref(size)):
return None return None
elif typ.value == REG_MULTI_SZ: elif typ.value == REG_MULTI_SZ:
return [x for x in ctypes.wstring_at(buf, len(buf)-2).split(u'\x00')] return [x for x in ctypes.wstring_at(buf, len(buf)-2).split(u'\x00')]
else: else:
return unicode(buf.value) return str(buf.value)
def getint(self, key): def getint(self, key):
typ = DWORD() typ = DWORD()
@ -248,7 +253,7 @@ class Config:
elif isinstance(val, numbers.Integral): elif isinstance(val, numbers.Integral):
RegSetValueEx(self.hkey, key, 0, REG_DWORD, ctypes.byref(DWORD(val)), 4) RegSetValueEx(self.hkey, key, 0, REG_DWORD, ctypes.byref(DWORD(val)), 4)
elif hasattr(val, '__iter__'): # iterable elif hasattr(val, '__iter__'): # iterable
stringval = u'\x00'.join([unicode(x) or u' ' for x in val] + [u'']) # null terminated non-empty strings stringval = u'\x00'.join([str(x) or u' ' for x in val] + [u'']) # null terminated non-empty strings
buf = ctypes.create_unicode_buffer(stringval) buf = ctypes.create_unicode_buffer(stringval)
RegSetValueEx(self.hkey, key, 0, REG_MULTI_SZ, buf, len(buf)*2) RegSetValueEx(self.hkey, key, 0, REG_MULTI_SZ, buf, len(buf)*2)
else: else:
@ -333,14 +338,14 @@ class Config:
def save(self): def save(self):
with codecs.open(self.filename, 'w', 'utf-8') as h: with codecs.open(self.filename, 'w', 'utf-8') as h:
h.write(unicode(self.config.data)) h.write(str(self.config.data))
def close(self): def close(self):
self.save() self.save()
self.config = None self.config = None
def _escape(self, val): def _escape(self, val):
return unicode(val).replace(u'\\', u'\\\\').replace(u'\n', u'\\n').replace(u';', u'\\;') return str(val).replace(u'\\', u'\\\\').replace(u'\n', u'\\n').replace(u';', u'\\;')
def _unescape(self, val): def _unescape(self, val):
chars = list(val) chars = list(val)

View File

@ -1,3 +1,6 @@
from __future__ import division
from __future__ import print_function
from past.utils import old_div
import json import json
from calendar import timegm from calendar import timegm
from operator import itemgetter from operator import itemgetter
@ -68,18 +71,18 @@ class Dashboard(FileSystemEventHandler):
self.observed = self.observer.schedule(self, self.currentdir) self.observed = self.observer.schedule(self, self.currentdir)
if __debug__: if __debug__:
print '%s Dashboard "%s"' % (polling and 'Polling' or 'Monitoring', self.currentdir) print('%s Dashboard "%s"' % (polling and 'Polling' or 'Monitoring', self.currentdir))
# Even if we're not intending to poll, poll at least once to process pre-existing # Even if we're not intending to poll, poll at least once to process pre-existing
# data and to check whether the watchdog thread has crashed due to events not # data and to check whether the watchdog thread has crashed due to events not
# being supported on this filesystem. # being supported on this filesystem.
self.root.after(self._POLL * 1000/2, self.poll, True) self.root.after(old_div(self._POLL * 1000,2), self.poll, True)
return True return True
def stop(self): def stop(self):
if __debug__: if __debug__:
print 'Stopping monitoring Dashboard' print('Stopping monitoring Dashboard')
self.currentdir = None self.currentdir = None
if self.observed: if self.observed:
self.observed = None self.observed = None

35
l10n.py
View File

@ -3,6 +3,11 @@
# Localization with gettext is a pain on non-Unix systems. Use OSX-style strings files instead. # Localization with gettext is a pain on non-Unix systems. Use OSX-style strings files instead.
# #
from __future__ import print_function
from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import object
import codecs import codecs
from collections import OrderedDict from collections import OrderedDict
import numbers import numbers
@ -12,14 +17,14 @@ import re
import sys import sys
from sys import platform from sys import platform
from traceback import print_exc from traceback import print_exc
import __builtin__ import builtins
import locale import locale
try: try:
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
except: except:
# Locale env variables incorrect or locale package not installed/configured on Linux, mysterious reasons on Windows # Locale env variables incorrect or locale package not installed/configured on Linux, mysterious reasons on Windows
print "Can't set locale!" print("Can't set locale!")
from config import config from config import config
@ -48,7 +53,7 @@ elif platform == 'win32':
GetNumberFormatEx.restype = ctypes.c_int GetNumberFormatEx.restype = ctypes.c_int
class Translations: class Translations(object):
FALLBACK = 'en' # strings in this code are in English FALLBACK = 'en' # strings in this code are in English
FALLBACK_NAME = 'English' FALLBACK_NAME = 'English'
@ -63,7 +68,7 @@ class Translations:
def install_dummy(self): def install_dummy(self):
# For when translation is not desired or not available # For when translation is not desired or not available
self.translations = { None: {} } self.translations = { None: {} }
__builtin__.__dict__['_'] = lambda x: unicode(x).replace(ur'\"', u'"').replace(u'{CR}', u'\n') # Promote strings to Unicode for consistency builtins.__dict__['_'] = lambda x: str(x).replace(r'\"', u'"').replace(u'{CR}', u'\n') # Promote strings to Unicode for consistency
def install(self, lang=None): def install(self, lang=None):
available = self.available() available = self.available()
@ -91,11 +96,11 @@ class Translations:
if isdir(plugin_path): if isdir(plugin_path):
try: try:
self.translations[plugin] = self.contents(lang, plugin_path) self.translations[plugin] = self.contents(lang, plugin_path)
except UnicodeDecodeError, e: except UnicodeDecodeError as e:
print 'Malformed file %s.strings in plugin %s: %s' % (lang, plugin, e) print('Malformed file %s.strings in plugin %s: %s' % (lang, plugin, e))
except: except:
print_exc() print_exc()
__builtin__.__dict__['_'] = self.translate builtins.__dict__['_'] = self.translate
def contents(self, lang, plugin_path=None): def contents(self, lang, plugin_path=None):
assert lang in self.available() assert lang in self.available()
@ -108,11 +113,11 @@ class Translations:
if line.strip(): if line.strip():
match = Translations.TRANS_RE.match(line) match = Translations.TRANS_RE.match(line)
if match: if match:
translations[match.group(1).replace(ur'\"', u'"')] = match.group(2).replace(ur'\"', u'"').replace(u'{CR}', u'\n') translations[match.group(1).replace(r'\"', u'"')] = match.group(2).replace(r'\"', u'"').replace(u'{CR}', u'\n')
elif __debug__ and not Translations.COMMENT_RE.match(line): elif __debug__ and not Translations.COMMENT_RE.match(line):
print 'Bad translation: %s' % line.strip() print('Bad translation: %s' % line.strip())
if translations.get(LANGUAGE_ID, LANGUAGE_ID) == LANGUAGE_ID: if translations.get(LANGUAGE_ID, LANGUAGE_ID) == LANGUAGE_ID:
translations[LANGUAGE_ID] = unicode(lang) # Replace language name with code if missing translations[LANGUAGE_ID] = str(lang) # Replace language name with code if missing
return translations return translations
def translate(self, x, context=None): def translate(self, x, context=None):
@ -120,13 +125,13 @@ class Translations:
context = context[len(config.plugin_dir)+1:].split(os.sep)[0] context = context[len(config.plugin_dir)+1:].split(os.sep)[0]
if __debug__: if __debug__:
if self.translations[None] and context not in self.translations: if self.translations[None] and context not in self.translations:
print 'No translations for "%s"' % context print('No translations for "%s"' % context)
return self.translations.get(context, {}).get(x) or self.translate(x) return self.translations.get(context, {}).get(x) or self.translate(x)
else: else:
if __debug__: if __debug__:
if self.translations[None] and x not in self.translations[None]: if self.translations[None] and x not in self.translations[None]:
print 'Missing translation: "%s"' % x print('Missing translation: "%s"' % x)
return self.translations[None].get(x) or unicode(x).replace(ur'\"', u'"').replace(u'{CR}', u'\n') return self.translations[None].get(x) or str(x).replace(r'\"', u'"').replace(u'{CR}', u'\n')
# Returns list of available language codes # Returns list of available language codes
def available(self): def available(self):
@ -173,7 +178,7 @@ class Translations:
return codecs.open(join(self.respath(), '%s.strings' % lang), 'r', 'utf-8') return codecs.open(join(self.respath(), '%s.strings' % lang), 'r', 'utf-8')
class Locale: class Locale(object):
def __init__(self): def __init__(self):
if platform=='darwin': if platform=='darwin':
@ -274,7 +279,7 @@ if __name__ == "__main__":
os.mkdir(LOCALISATION_DIR) os.mkdir(LOCALISATION_DIR)
template = codecs.open(join(LOCALISATION_DIR, 'en.template'), 'w', 'utf-8') template = codecs.open(join(LOCALISATION_DIR, 'en.template'), 'w', 'utf-8')
template.write('/* Language name */\n"%s" = "%s";\n\n' % (LANGUAGE_ID, 'English')) template.write('/* Language name */\n"%s" = "%s";\n\n' % (LANGUAGE_ID, 'English'))
for thing in sorted(seen, key=unicode.lower): for thing in sorted(seen, key=str.lower):
if seen[thing]: if seen[thing]:
template.write('/* %s */\n' % (seen[thing])) template.write('/* %s */\n' % (seen[thing]))
template.write('"%s" = "%s";\n\n' % (thing, thing)) template.write('"%s" = "%s";\n\n' % (thing, thing))

View File

@ -1,3 +1,4 @@
from __future__ import print_function
from collections import defaultdict, OrderedDict from collections import defaultdict, OrderedDict
import json import json
import re import re
@ -152,8 +153,8 @@ class EDLogs(FileSystemEventHandler):
self.observed = self.observer.schedule(self, self.currentdir) self.observed = self.observer.schedule(self, self.currentdir)
if __debug__: if __debug__:
print '%s Journal "%s"' % (polling and 'Polling' or 'Monitoring', self.currentdir) print('%s Journal "%s"' % (polling and 'Polling' or 'Monitoring', self.currentdir))
print 'Start logfile "%s"' % self.logfile print('Start logfile "%s"' % self.logfile)
if not self.running(): if not self.running():
self.thread = threading.Thread(target = self.worker, name = 'Journal worker') self.thread = threading.Thread(target = self.worker, name = 'Journal worker')
@ -164,7 +165,7 @@ class EDLogs(FileSystemEventHandler):
def stop(self): def stop(self):
if __debug__: if __debug__:
print 'Stopping monitoring Journal' print('Stopping monitoring Journal')
self.currentdir = None self.currentdir = None
self.version = self.mode = self.group = self.cmdr = self.planet = self.system = self.station = self.stationtype = self.stationservices = self.coordinates = self.systemaddress = None self.version = self.mode = self.group = self.cmdr = self.planet = self.system = self.station = self.stationtype = self.stationservices = self.coordinates = self.systemaddress = None
self.is_beta = False self.is_beta = False
@ -206,7 +207,7 @@ class EDLogs(FileSystemEventHandler):
self.parse_entry(line) # Some events are of interest even in the past self.parse_entry(line) # Some events are of interest even in the past
except: except:
if __debug__: if __debug__:
print 'Invalid journal entry "%s"' % repr(line) print('Invalid journal entry "%s"' % repr(line))
logpos = loghandle.tell() logpos = loghandle.tell()
else: else:
loghandle = None loghandle = None
@ -262,7 +263,7 @@ class EDLogs(FileSystemEventHandler):
fcntl(loghandle, F_GLOBAL_NOCACHE, -1) # required to avoid corruption on macOS over SMB fcntl(loghandle, F_GLOBAL_NOCACHE, -1) # required to avoid corruption on macOS over SMB
logpos = 0 logpos = 0
if __debug__: if __debug__:
print 'New logfile "%s"' % logfile print('New logfile "%s"' % logfile)
if logfile: if logfile:
loghandle.seek(0, SEEK_END) # required to make macOS notice log change over SMB loghandle.seek(0, SEEK_END) # required to make macOS notice log change over SMB
@ -454,10 +455,10 @@ class EDLogs(FileSystemEventHandler):
payload = dict(entry) payload = dict(entry)
payload.pop('event') payload.pop('event')
payload.pop('timestamp') payload.pop('timestamp')
for k,v in payload.iteritems(): for k,v in payload.items():
self.state['Rank'][k] = (v,0) self.state['Rank'][k] = (v,0)
elif entry['event'] == 'Progress': elif entry['event'] == 'Progress':
for k,v in entry.iteritems(): for k,v in entry.items():
if k in self.state['Rank']: if k in self.state['Rank']:
self.state['Rank'][k] = (self.state['Rank'][k][0], min(v, 100)) # perhaps not taken promotion mission yet self.state['Rank'][k] = (self.state['Rank'][k][0], min(v, 100)) # perhaps not taken promotion mission yet
elif entry['event'] in ['Reputation', 'Statistics']: elif entry['event'] in ['Reputation', 'Statistics']:
@ -621,7 +622,7 @@ class EDLogs(FileSystemEventHandler):
return entry return entry
except: except:
if __debug__: if __debug__:
print 'Invalid journal entry "%s"' % repr(line) print('Invalid journal entry "%s"' % repr(line))
print_exc() print_exc()
return { 'event': None } return { 'event': None }

View File

@ -7,8 +7,8 @@
from sys import platform from sys import platform
import Tkinter as tk import tkinter as tk
import ttk from tkinter import ttk
# Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult # Can't do this with styles on OSX - http://www.tkdocs.com/tutorial/styles.html#whydifficult

View File

@ -8,7 +8,7 @@ import operator
import threading # We don't use it, but plugins might import threading # We don't use it, but plugins might
from traceback import print_exc from traceback import print_exc
import Tkinter as tk import tkinter as tk
import myNotebook as nb import myNotebook as nb
from config import config from config import config

View File

@ -5,9 +5,9 @@ from os.path import dirname, expanduser, expandvars, exists, isdir, join, normpa
from sys import platform from sys import platform
import webbrowser import webbrowser
import Tkinter as tk import tkinter as tk
import ttk from tkinter import ttk
import tkColorChooser from tkinter import colorchooser as tkColorChooser
from ttkHyperlinkLabel import HyperlinkLabel from ttkHyperlinkLabel import HyperlinkLabel
import myNotebook as nb import myNotebook as nb

View File

@ -1,8 +1,12 @@
from __future__ import print_function
# edmc: protocol handler for cAPI authorisation # edmc: protocol handler for cAPI authorisation
from future import standard_library
standard_library.install_aliases()
from builtins import object
import threading import threading
import urllib2 import urllib.request, urllib.error, urllib.parse
import sys import sys
from config import appname from config import appname
@ -17,7 +21,7 @@ if sys.platform == 'win32':
is_wine = False is_wine = False
class GenericProtocolHandler: class GenericProtocolHandler(object):
def __init__(self): def __init__(self):
self.redirect = 'edmc://auth' # Base redirection URL self.redirect = 'edmc://auth' # Base redirection URL
@ -67,7 +71,7 @@ if sys.platform == 'darwin' and getattr(sys, 'frozen', False):
return self return self
def handleEvent_withReplyEvent_(self, event, replyEvent): def handleEvent_withReplyEvent_(self, event, replyEvent):
protocolhandler.lasturl = urllib2.unquote(event.paramDescriptorForKeyword_(keyDirectObject).stringValue()).strip() protocolhandler.lasturl = urllib.parse.unquote(event.paramDescriptorForKeyword_(keyDirectObject).stringValue()).strip()
protocolhandler.master.after(ProtocolHandler.POLL, protocolhandler.poll) protocolhandler.master.after(ProtocolHandler.POLL, protocolhandler.poll)
@ -189,7 +193,7 @@ elif sys.platform == 'win32' and getattr(sys, 'frozen', False) and not is_wine:
args = wstring_at(GlobalLock(msg.lParam)).strip() args = wstring_at(GlobalLock(msg.lParam)).strip()
GlobalUnlock(msg.lParam) GlobalUnlock(msg.lParam)
if args.lower().startswith('open("') and args.endswith('")'): if args.lower().startswith('open("') and args.endswith('")'):
url = urllib2.unquote(args[6:-2]).strip() url = urllib.parse.unquote(args[6:-2]).strip()
if url.startswith(self.redirect): if url.startswith(self.redirect):
self.event(url) self.event(url)
SetForegroundWindow(GetParent(self.master.winfo_id())) # raise app window SetForegroundWindow(GetParent(self.master.winfo_id())) # raise app window
@ -202,11 +206,11 @@ elif sys.platform == 'win32' and getattr(sys, 'frozen', False) and not is_wine:
TranslateMessage(byref(msg)) TranslateMessage(byref(msg))
DispatchMessage(byref(msg)) DispatchMessage(byref(msg))
else: else:
print 'Failed to register DDE for cAPI' print('Failed to register DDE for cAPI')
else: # Linux / Run from source else: # Linux / Run from source
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from http.server import HTTPServer, BaseHTTPRequestHandler
class ProtocolHandler(GenericProtocolHandler): class ProtocolHandler(GenericProtocolHandler):
@ -236,7 +240,7 @@ else: # Linux / Run from source
class HTTPRequestHandler(BaseHTTPRequestHandler): class HTTPRequestHandler(BaseHTTPRequestHandler):
def parse(self): def parse(self):
url = urllib2.unquote(self.path) url = urllib.parse.unquote(self.path)
if url.startswith('/auth'): if url.startswith('/auth'):
protocolhandler.event(url) protocolhandler.event(url)
self.send_response(200) self.send_response(200)

View File

@ -6,8 +6,8 @@ import time
if __debug__: if __debug__:
from traceback import print_exc from traceback import print_exc
import Tkinter as tk import tkinter as tk
import ttk from tkinter import ttk
import myNotebook as nb import myNotebook as nb
import companion import companion

View File

@ -1,3 +1,4 @@
from __future__ import division
# #
# Theme support # Theme support
# #
@ -5,12 +6,17 @@
# So can't use ttk's theme support. So have to change colors manually. # So can't use ttk's theme support. So have to change colors manually.
# #
from future import standard_library
standard_library.install_aliases()
from builtins import str
from builtins import object
from past.utils import old_div
from sys import platform from sys import platform
from os.path import join from os.path import join
import Tkinter as tk import tkinter as tk
import ttk from tkinter import ttk
import tkFont from tkinter import font as tkFont
from ttkHyperlinkLabel import HyperlinkLabel from ttkHyperlinkLabel import HyperlinkLabel
from config import appname, applongname, config from config import appname, applongname, config
@ -98,7 +104,7 @@ elif platform == 'linux2':
dpy = None dpy = None
class _Theme: class _Theme(object):
def __init__(self): def __init__(self):
self.active = None # Starts out with no theme self.active = None # Starts out with no theme
@ -210,7 +216,7 @@ class _Theme:
'foreground' : config.get('dark_text'), 'foreground' : config.get('dark_text'),
'activebackground' : config.get('dark_text'), 'activebackground' : config.get('dark_text'),
'activeforeground' : 'grey4', 'activeforeground' : 'grey4',
'disabledforeground' : '#%02x%02x%02x' % (r/384, g/384, b/384), 'disabledforeground' : '#%02x%02x%02x' % (old_div(r,384), old_div(g,384), old_div(b,384)),
'highlight' : config.get('dark_highlight'), 'highlight' : config.get('dark_highlight'),
# Font only supports Latin 1 / Supplement / Extended, and a few General Punctuation and Mathematical Operators # Font only supports Latin 1 / Supplement / Extended, and a few General Punctuation and Mathematical Operators
'font' : (theme > 1 and not 0x250 < ord(_('Cmdr')[0]) < 0x3000 and 'font' : (theme > 1 and not 0x250 < ord(_('Cmdr')[0]) < 0x3000 and

View File

@ -1,9 +1,9 @@
from sys import platform from sys import platform
import webbrowser import webbrowser
import Tkinter as tk import tkinter as tk
import ttk from tkinter import ttk
import tkFont from tkinter import font as tkFont
if platform == 'win32': if platform == 'win32':
import subprocess import subprocess