[Core] Refactor the base argparser and translation code.

- Move baseargparser out of deluge/ui since it is also used by the
  Daemon and could cause packaging issues if UI code is not available.
  - Renamed baseargparser to argparserbase to follow existing Deluge
    naming.
  - Renamed get_version to distinguish from deluge.common.get_version.
- Translation code is usable by more than just the UIs so also move it
  to Deluge namespace and re-use i18n directory and make it a package.
  - Renamed setup_translations to singular as it felt more correct.
  - Renamed set_dummy_trans to be more descriptive.

Closes: #3081
This commit is contained in:
Calum Lind 2019-05-10 12:58:33 +01:00
parent 653f80eac8
commit d417c4b0f9
18 changed files with 82 additions and 62 deletions

View File

@ -86,7 +86,7 @@ argparse.ArgumentParser.find_subcommand = find_subcommand
argparse.ArgumentParser.set_default_subparser = set_default_subparser
def get_version():
def _get_version_detail():
version_str = '%s\n' % (common.get_version())
try:
from deluge._libtorrent import LT_VERSION
@ -151,7 +151,7 @@ class HelpAction(argparse._HelpAction):
parser.exit()
class BaseArgParser(argparse.ArgumentParser):
class ArgParserBase(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
if 'formatter_class' not in kwargs:
kwargs['formatter_class'] = lambda prog: DelugeTextHelpFormatter(
@ -165,7 +165,7 @@ class BaseArgParser(argparse.ArgumentParser):
self.log_stream = kwargs['log_stream']
del kwargs['log_stream']
super(BaseArgParser, self).__init__(*args, **kwargs)
super(ArgParserBase, self).__init__(*args, **kwargs)
self.common_setup = False
self.process_arg_group = False
@ -178,13 +178,13 @@ class BaseArgParser(argparse.ArgumentParser):
'-V',
'--version',
action='version',
version='%(prog)s ' + get_version(),
version='%(prog)s ' + _get_version_detail(),
help=_('Print version information'),
)
self.group.add_argument(
'-v',
action='version',
version='%(prog)s ' + get_version(),
version='%(prog)s ' + _get_version_detail(),
help=argparse.SUPPRESS,
) # Deprecated arg
self.group.add_argument(
@ -246,7 +246,7 @@ class BaseArgParser(argparse.ArgumentParser):
argparse.Namespace: The parsed arguments.
"""
options = super(BaseArgParser, self).parse_args(args=args)
options = super(ArgParserBase, self).parse_args(args=args)
return self._handle_ui_options(options)
def parse_known_ui_args(self, args, withhold=None):
@ -262,7 +262,7 @@ class BaseArgParser(argparse.ArgumentParser):
"""
if withhold:
args = [a for a in args if a not in withhold]
options, remaining = super(BaseArgParser, self).parse_known_args(args=args)
options, remaining = super(ArgParserBase, self).parse_known_args(args=args)
options.remaining = remaining
# Hanlde common and process group options
return self._handle_ui_options(options)

View File

@ -15,10 +15,10 @@ from logging import DEBUG, FileHandler, getLogger
from twisted.internet.error import CannotListenError
from deluge.argparserbase import ArgParserBase
from deluge.common import run_profiled
from deluge.configmanager import get_config_dir
from deluge.ui.baseargparser import BaseArgParser
from deluge.ui.translations_util import set_dummy_trans
from deluge.i18n import setup_mock_translation
def add_daemon_options(parser):
@ -78,10 +78,10 @@ def start_daemon(skip_start=False):
deluge.core.daemon.Daemon: A new daemon object
"""
set_dummy_trans(warn_msg=True)
setup_mock_translation(warn_msg=True)
# Setup the argument parser
parser = BaseArgParser()
parser = ArgParserBase()
add_daemon_options(parser)
options = parser.parse_args()

15
deluge/i18n/__init__.py Normal file
View File

@ -0,0 +1,15 @@
from .util import (
I18N_DOMAIN,
get_languages,
set_language,
setup_mock_translation,
setup_translation,
)
__all__ = [
'I18N_DOMAIN',
'set_language',
'get_languages',
'setup_translation',
'setup_mock_translation',
]

View File

@ -9,6 +9,12 @@ from __future__ import unicode_literals
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'en-us'
# Deferred translation
def _(message):
return message
# Languages we provide translations for, out of the box.
LANGUAGES = {
'af': _('Afrikaans'),
@ -107,3 +113,5 @@ LANGUAGES = {
'zh-hant': _('Traditional Chinese'),
'zh_TW': _('Chinese (Taiwan)'),
}
del _

View File

@ -20,35 +20,19 @@ from six.moves import builtins
import deluge.common
from .languages import LANGUAGES
log = logging.getLogger(__name__)
log.addHandler(
logging.NullHandler()
) # Silence: No handlers could be found for logger "deluge.util.lang"
I18N_DOMAIN = 'deluge'
def set_dummy_trans(warn_msg=None):
def _func(*txt):
if warn_msg:
log.warning(
'"%s" has been marked for translation, but translation is unavailable.',
txt[0],
)
return txt[0]
builtins.__dict__['_'] = _func
builtins.__dict__['ngettext'] = builtins.__dict__['_n'] = _func
def get_translations_path():
"""Get the absolute path to the directory containing translation files"""
return deluge.common.resource_filename('deluge', 'i18n')
def get_languages():
from deluge.ui import languages # Import here so that gettext has been setup first
lang = []
translations_path = get_translations_path()
@ -61,9 +45,9 @@ def get_languages():
for i, lang_code in enumerate(lang_dirs):
name = '%s (Language name missing)' % lang_code
if lang_code in languages.LANGUAGES:
name = languages.LANGUAGES[lang_code]
lang.append([lang_code, name])
if lang_code in LANGUAGES:
name = LANGUAGES[lang_code]
lang.append([lang_code, _(name)])
lang = sorted(lang, key=lambda l: l[1])
return lang
@ -93,8 +77,21 @@ def set_language(lang):
log.warning('IOError when loading translations: %s', ex)
def setup_mock_translation(warn_msg=None):
def _func(*txt):
if warn_msg:
log.warning(
'"%s" has been marked for translation, but translation is unavailable.',
txt[0],
)
return txt[0]
builtins.__dict__['_'] = _func
builtins.__dict__['ngettext'] = builtins.__dict__['_n'] = _func
# Initialize gettext
def setup_translations():
def setup_translation():
translations_path = get_translations_path()
log.info('Setting up translations from %s', translations_path)
@ -131,6 +128,6 @@ def setup_translations():
except Exception as ex:
log.error('Unable to initialize gettext/locale!')
log.exception(ex)
set_dummy_trans()
setup_mock_translation()
deluge.common.translate_size_units()

View File

@ -23,7 +23,7 @@ import deluge.configmanager
import deluge.core.preferencesmanager
import deluge.log
from deluge.error import DelugeError
from deluge.ui.translations_util import setup_translations
from deluge.i18n import setup_translation
# This sets log level to critical, so use log.critical() to debug while running unit tests
deluge.log.setup_logger('none')
@ -75,7 +75,7 @@ def add_watchdog(deferred, timeout=0.05, message=None):
# Initialize gettext
setup_translations()
setup_translation()
class ReactorOverride(object):

View File

@ -30,7 +30,7 @@ from deluge.common import (
is_url,
windows_check,
)
from deluge.ui.translations_util import setup_translations
from deluge.i18n import setup_translation
from .common import get_test_data_file, set_tmp_config_dir
@ -38,7 +38,7 @@ from .common import get_test_data_file, set_tmp_config_dir
class CommonTestCase(unittest.TestCase):
def setUp(self): # NOQA
self.config_dir = set_tmp_config_dir()
setup_translations()
setup_translation()
def tearDown(self): # NOQA
pass

View File

@ -13,7 +13,7 @@ from twisted.trial import unittest
import deluge.component as component
from deluge.common import windows_check
from deluge.configmanager import ConfigManager
from deluge.ui.translations_util import setup_translations
from deluge.i18n import setup_translation
from . import common
from .basetest import BaseTestCase
@ -27,7 +27,7 @@ try:
except ImportError:
libs_available = False
setup_translations()
setup_translation()
@pytest.mark.gtkui

View File

@ -15,7 +15,7 @@ from twisted.trial import unittest
import deluge.component as component
from deluge.configmanager import ConfigManager
from deluge.ui.translations_util import setup_translations
from deluge.i18n import setup_translation
from . import common
from .basetest import BaseTestCase
@ -36,7 +36,7 @@ except (ImportError, ValueError):
else:
libs_available = True
setup_translations()
setup_translation()
@pytest.mark.gtkui

View File

@ -15,7 +15,7 @@ import os
import sys
import deluge.common
from deluge.ui.baseargparser import BaseArgParser, DelugeTextHelpFormatter
from deluge.argparserbase import ArgParserBase, DelugeTextHelpFormatter
from deluge.ui.ui import UI
log = logging.getLogger(__name__)
@ -143,7 +143,7 @@ class Console(UI):
def start(self):
if self.ui_args is None:
# Started directly by deluge-console script so must find the UI args manually
options, remaining = BaseArgParser(common_help=False).parse_known_args()
options, remaining = ArgParserBase(common_help=False).parse_known_args()
self.ui_args = remaining
i = self.console_parser.find_subcommand(args=self.ui_args)

View File

@ -13,6 +13,7 @@ import logging
from deluge.common import is_ip
from deluge.decorators import overrides
from deluge.i18n import get_languages
from deluge.ui.client import client
from deluge.ui.common import DISK_CACHE_KEYS
from deluge.ui.console.widgets import BaseInputPane, BaseWindow
@ -192,7 +193,6 @@ class InterfacePane(BasePreferencePane):
_('Move selection when moving torrents in the queue'),
console_config['torrentview']['move_selection'],
)
from deluge.ui.translations_util import get_languages
langs = get_languages()
langs.insert(0, ('', 'System Default'))

View File

@ -13,11 +13,11 @@ import copy
import logging
import deluge.common
from deluge.i18n import setup_translation
from deluge.ui.common import TORRENT_DATA_FIELD
from deluge.ui.console.utils import format_utils
from deluge.ui.translations_util import setup_translations
setup_translations()
setup_translation()
log = logging.getLogger(__name__)

View File

@ -47,11 +47,11 @@ from deluge.common import (
)
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.error import DaemonRunningError
from deluge.i18n import I18N_DOMAIN, set_language, setup_translation
from deluge.ui.client import client
from deluge.ui.hostlist import LOCALHOST
from deluge.ui.sessionproxy import SessionProxy
from deluge.ui.tracker_icons import TrackerIcons
from deluge.ui.translations_util import I18N_DOMAIN, set_language, setup_translations
# isort:imports-localfolder
from .addtorrentdialog import AddTorrentDialog
@ -146,7 +146,7 @@ def windowing(like):
class GtkUI(object):
def __init__(self, args):
# Setup gtkbuilder/glade translation
setup_translations()
setup_translation()
Builder().set_translation_domain(I18N_DOMAIN)
# Setup signals

View File

@ -22,9 +22,9 @@ import deluge.common
import deluge.component as component
from deluge.configmanager import ConfigManager, get_config_dir
from deluge.error import AuthManagerError, NotAuthorizedError
from deluge.i18n import get_languages
from deluge.ui.client import client
from deluge.ui.common import DISK_CACHE_KEYS, PREFS_CATOG_TRANS
from deluge.ui.translations_util import get_languages
from .common import associate_magnet_links, get_clipboard_text, get_deluge_icon
from .dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog

View File

@ -14,8 +14,8 @@ import logging
import deluge.common
import deluge.configmanager
import deluge.log
from deluge.ui.baseargparser import BaseArgParser
from deluge.ui.translations_util import setup_translations
from deluge.argparserbase import ArgParserBase
from deluge.i18n import setup_translation
log = logging.getLogger(__name__)
@ -38,8 +38,8 @@ class UI(object):
def __init__(self, name, **kwargs):
self.__name = name
self.ui_args = kwargs.pop('ui_args', None)
setup_translations()
self.__parser = BaseArgParser(**kwargs)
setup_translation()
self.__parser = ArgParserBase(**kwargs)
def parse_args(self, parser, args=None):
options = parser.parse_args(args)

View File

@ -23,8 +23,8 @@ import pkg_resources
import deluge.common
import deluge.configmanager
from deluge.ui.baseargparser import BaseArgParser
from deluge.ui.translations_util import setup_translations
from deluge.argparserbase import ArgParserBase
from deluge.i18n import setup_translation
DEFAULT_PREFS = {'default_ui': 'gtk'}
@ -33,7 +33,7 @@ AMBIGUOUS_CMD_ARGS = ('-h', '--help', '-v', '-V', '--version')
def start_ui():
"""Entry point for ui script"""
setup_translations()
setup_translation()
# Get the registered UI entry points
ui_entrypoints = {}
@ -59,7 +59,7 @@ def start_ui():
return _parser
# Setup parser with Common Options and add UI Options group.
parser = add_ui_options_group(BaseArgParser())
parser = add_ui_options_group(ArgParserBase())
# Parse and handle common/process group options
options = parser.parse_known_ui_args(sys.argv, withhold=AMBIGUOUS_CMD_ARGS)
@ -81,7 +81,7 @@ def start_ui():
# We have parsed and got the config dir needed to get the default UI
# Now create a parser for choosing the UI. We reuse the ui option group for
# parsing to succeed and the text displayed to user, but result is not used.
parser = add_ui_options_group(BaseArgParser(common_help=True))
parser = add_ui_options_group(ArgParserBase(common_help=True))
# Create subparser for each registered UI. Empty title is used to remove unwanted positional text.
subparsers = parser.add_subparsers(

View File

@ -26,12 +26,12 @@ from deluge import component, httpdownloader
from deluge.common import AUTH_LEVEL_DEFAULT, get_magnet_info, is_magnet
from deluge.configmanager import get_config_dir
from deluge.error import NotAuthorizedError
from deluge.i18n import get_languages
from deluge.ui.client import Client, client
from deluge.ui.common import FileTree2, TorrentInfo
from deluge.ui.coreconfig import CoreConfig
from deluge.ui.hostlist import HostList
from deluge.ui.sessionproxy import SessionProxy
from deluge.ui.translations_util import get_languages
from deluge.ui.web.common import _
log = logging.getLogger(__name__)

View File

@ -25,8 +25,8 @@ from deluge import common, component, configmanager
from deluge.common import is_ipv6
from deluge.core.rpcserver import check_ssl_keys
from deluge.crypto_utils import get_context_factory
from deluge.i18n import set_language, setup_translation
from deluge.ui.tracker_icons import TrackerIcons
from deluge.ui.translations_util import set_language, setup_translations
from deluge.ui.web.auth import Auth
from deluge.ui.web.common import Template
from deluge.ui.web.json_api import JSON, WebApi, WebUtils
@ -687,7 +687,7 @@ class DelugeWeb(component.Component):
# Strip away slashes and serve on the base path as well as root path
self.top_level.putChild(self.base.strip('/'), self.top_level)
setup_translations()
setup_translation()
# Remove twisted version number from 'server' http-header for security reasons
server.version = 'TwistedWeb'