Skip to content
This repository has been archived by the owner on Oct 1, 2024. It is now read-only.

Commit

Permalink
AssetManager refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Michał Szymaniak committed Jan 26, 2019
1 parent 6d1bfe5 commit e9b99b7
Show file tree
Hide file tree
Showing 17 changed files with 707 additions and 551 deletions.
43 changes: 4 additions & 39 deletions subsync/assets/__init__.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,9 @@
from subsync import config
from subsync import utils
from subsync.assets import local
from subsync.assets import remote
from subsync.assets.downloader import AssetDownloader
from subsync.assets.updater import SelfUpdater
from subsync.assets.mgr import AssetManager

remoteAssets = None

assetManager = AssetManager()

def init(updateCb):
global remoteAssets
remoteAssets = remote.RemoteAssets(updateCb)

def terminate():
global remoteAssets
if remoteAssets:
remoteAssets.terminate()
remoteAssets = None

def getLocalAsset(*args, **kwargs):
return local.getAsset(*args, **kwargs)

def getRemoteAsset(*args, **kwargs):
return remoteAssets.getAsset(*args, **kwargs)

def isUpdateAvailable():
if config.assetupd:
currentVer = utils.getCurrentVersion()
remoteUpdate = getRemoteAsset(**config.assetupd)
remoteVer = utils.parseVersion(remoteUpdate.get('version'))
return currentVer and remoteVer and currentVer < remoteVer

def getAssetPrettyName(type, params, **kw):
if type == 'speech':
return _('{} speech recognition model').format(
utils.getLanguageName(params[0]))
elif type == 'dict':
return _('dictionary {} / {}').format(
utils.getLanguageName(params[0]),
utils.getLanguageName(params[1]))
else:
return '{}/{}'.format(type, '-'.join(params))
def getAsset(assetId, params=None):
return assetManager.getAsset(assetId, params)

81 changes: 0 additions & 81 deletions subsync/assets/downloader.py

This file was deleted.

222 changes: 222 additions & 0 deletions subsync/assets/item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
from subsync.assets.updater import Updater
from subsync import config
from subsync import utils
from subsync.error import Error
import os
import json
import shutil
import subprocess
import stat

import logging
logger = logging.getLogger(__name__)


class Asset(object):
def __init__(self, type, params):
self.type = type
self.params = params

self.local = None
self.remote = None
self.updater = None

fname = '{}.{}'.format('-'.join(params), type)
self.path = os.path.join(config.assetdir, type, fname)
self.localDir = None

def updateLocal(self):
self.local = {}

def updateRemote(self, remote):
self.remote = remote

def getPrettyName(self):
return mkId(self.type, self.params)

def getLocal(self, key=None, defaultValue=None):
if self.local == None:
self.updateLocal()
local = self.local or {}
if key:
return local.get(key, defaultValue)
else:
return local

def getRemote(self, key=None, defaultValue=None):
remote = self.remote or {}
if key:
return remote.get(key, defaultValue)
else:
return remote

def getUpdater(self):
if not self.updater and self.remote:
self.updater = Updater(self)
return self.updater

def isLocal(self):
return bool(self.getLocal())

def isRemote(self):
return bool(self.getRemote())

def localVersion(self, defaultVersion=(0, 0, 0)):
return utils.parseVersion(self.getLocal('version'), defaultVersion)

def remoteVersion(self, defaultVersion=(0, 0, 0)):
return utils.parseVersion(self.getRemote('version'), defaultVersion)

def validateLocal(self):
pass

def removeLocal(self):
try:
if self.path and os.path.isfile(self.path):
logger.info('removing %s', self.path)
os.remove(self.path)
except Exception as e:
logger.error('cannot remove %s: %r', self.getPrettyName(), e, exc_info=True)

try:
if self.localDir and os.path.isdir(self.localDir):
logger.info('removing %s', self.localDir)
shutil.rmtree(self.localDir, ignore_errors=True)
except Exception as e:
logger.error('cannot remove %s: %r', self.getPrettyName(), e, exc_info=True)

self.local = {}

def isUpgradable(self):
return self.isRemote() and self.remoteVersion() > self.localVersion()

def __repr__(self):
return '<Asset {} local={} remote={} path={}>'.format(
mkId(self.type, self.params),
self.getLocal(),
self.isRemote(),
self.path)


class DictAsset(Asset):
def updateLocal(self):
try:
with open(self.path, encoding='utf8') as fp:
ents = fp.readline().strip().split('/', 3)

if len(ents) >= 4 and ents[0] == '#dictionary':
self.local = dict(
lang1 = ents[1],
lang2 = ents[2],
version = ents[3])

except Exception as e:
logger.warn('cannot load %s: %r', self.getPrettyName(), e)
self.removeLocal()

def getPrettyName(self):
if len(self.params) >= 2:
return _('dictionary {} / {}').format(
utils.getLanguageName(self.params[0]),
utils.getLanguageName(self.params[1]))
else:
super().getPrettyName()


class SpeechAsset(Asset):
def updateLocal(self):
try:
with open(self.path, encoding='utf8') as fp:
local = json.load(fp)

# fix local paths
dirname = os.path.abspath(os.path.dirname(self.path))
sphinx = local.get('sphinx', None)
if sphinx:
for key, val in sphinx.items():
if val.startswith('./'):
sphinx[key] = os.path.join(dirname, *val.split('/')[1:])

localDir = local.get('dir', None)
if localDir and localDir.startswith('./'):
localDir = os.path.join(dirname, *localDir.split('/')[1:])

self.local = local
self.localDir = localDir

except Exception as e:
logger.warn('cannot load %s: %r', self.getPrettyName(), e)
self.removeLocal()

def getPrettyName(self):
if len(self.params) >= 1:
return _('{} speech recognition model').format(
utils.getLanguageName(self.params[0]))
else:
super().getPrettyName()


class UpdateAsset(Asset):
def __init__(self, type, params):
super().__init__(type, params)
self.localDir = os.path.join(config.assetdir, 'upgrade')
self.path = os.path.join(self.localDir, 'upgrade.json')

def updateLocal(self):
try:
with open(self.path, encoding='utf8') as fp:
self.local = json.load(fp)

except Exception as e:
logger.warn('cannot load %s: %r', self.getPrettyName(), e)
self.removeLocal()

def installUpdate(self):
try:
instPath = os.path.join(self.localDir, self.getLocal('install'))
logger.info('executing installer %s', instPath)
mode = os.stat(instPath).st_mode
if (mode & stat.S_IEXEC) == 0:
os.chmod(instPath, mode | stat.S_IEXEC)
subprocess.Popen(instPath, cwd=self.localDir)

except Exception as e:
logger.error('cannot install update %s: %r', self.path, e, exc_info=True)
raise Error(_('Update instalation failed miserably'))

def hasUpdate(self):
return self.hasRemoteUpdate() or self.hasLocalUpdate()

def hasLocalUpdate(self):
cur = utils.getCurrentVersion()
return cur and self.isLocal() and self.localVersion() > cur

def hasRemoteUpdate(self):
cur = utils.getCurrentVersion()
return cur and self.isRemote() and self.remoteVersion() > cur


def getAssetTypeByName(typ, params=None):
types = {
'dict': DictAsset,
'speech': SpeechAsset,
'subsync': UpdateAsset,
}

T = types.get(typ, Asset)
return T(typ, params)


def mkId(type, params):
return '{}/{}'.format(type, '-'.join(params))


def parseId(id):
ents = id.split('/', 1)
if len(ents) == 2:
return ents[0], ents[1].split('-')
elif len(ents) == 1:
return ents[0], None
else:
return None, None

24 changes: 0 additions & 24 deletions subsync/assets/local.py

This file was deleted.

Loading

0 comments on commit e9b99b7

Please sign in to comment.