Mercurial > libervia-backend
diff sat_frontends/jp/base.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | b2f323237fce |
children | fee60f17ebac |
line wrap: on
line diff
--- a/sat_frontends/jp/base.py Wed Jul 31 11:31:22 2019 +0200 +++ b/sat_frontends/jp/base.py Tue Aug 13 19:08:41 2019 +0200 @@ -78,7 +78,7 @@ GLib.timeout_add(delay, callback, *args) else: - print u"can't start jp: only D-Bus bridge is currently handled" + print("can't start jp: only D-Bus bridge is currently handled") sys.exit(C.EXIT_ERROR) # FIXME: twisted loop can be used when jp can handle fully async bridges # from twisted.internet import reactor @@ -104,28 +104,20 @@ from sat.core import sat_main sat = sat_main.SAT() -if sys.version_info < (2, 7, 3): - # XXX: shlex.split only handle unicode since python 2.7.3 - # this is a workaround for older versions - old_split = shlex.split - new_split = (lambda s, *a, **kw: [t.decode('utf-8') for t in old_split(s.encode('utf-8'), *a, **kw)] - if isinstance(s, unicode) else old_split(s, *a, **kw)) - shlex.split = new_split - try: import progressbar except ImportError: - msg = (_(u'ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar\n') + - _(u'Progress bar deactivated\n--\n')) - print >>sys.stderr,msg.encode('utf-8') + msg = (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar\n') + + _('Progress bar deactivated\n--\n')) + print(msg.encode('utf-8'), file=sys.stderr) progressbar=None #consts -PROG_NAME = u"jp" +PROG_NAME = "jp" DESCRIPTION = """This software is a command line tool for XMPP. Get the latest version at """ + C.APP_URL -COPYLEFT = u"""Copyright (C) 2009-2019 Jérôme Poisson, Adrien Cossa +COPYLEFT = """Copyright (C) 2009-2019 Jérôme Poisson, Adrien Cossa This program comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome to redistribute it under certain conditions. """ @@ -133,11 +125,6 @@ PROGRESS_DELAY = 10 # the progression will be checked every PROGRESS_DELAY ms -def unicode_decoder(arg): - # Needed to have unicode strings from arguments - return arg.decode(locale.getpreferredencoding()) - - def date_decoder(arg): return date_utils.date_parse_ext(arg, default_tz=date_utils.TZ_LOCAL) @@ -167,7 +154,7 @@ # loop will always be needed bridge_module = dynamic_import.bridge(bridge_name, 'sat_frontends.bridge') if bridge_module is None: - log.error(u"Can't import {} bridge".format(bridge_name)) + log.error("Can't import {} bridge".format(bridge_name)) sys.exit(1) self.bridge = bridge_module.Bridge() @@ -178,7 +165,7 @@ description=DESCRIPTION) self._make_parents() self.add_parser_options() - self.subparsers = self.parser.add_subparsers(title=_(u'Available commands'), dest='subparser_name') + self.subparsers = self.parser.add_subparsers(title=_('Available commands'), dest='subparser_name') self._auto_loop = False # when loop is used for internal reasons self._need_loop = False @@ -196,11 +183,11 @@ def _bridgeEb(self, failure): if isinstance(failure, exceptions.BridgeExceptionNoService): - print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) + print((_("Can't connect to SàT backend, are you sure it's launched ?"))) elif isinstance(failure, exceptions.BridgeInitError): - print(_(u"Can't init bridge")) + print((_("Can't init bridge"))) else: - print(_(u"Error while initialising bridge: {}".format(failure))) + print((_("Error while initialising bridge: {}".format(failure)))) sys.exit(C.EXIT_BRIDGE_ERROR) @property @@ -264,14 +251,14 @@ if self.verbosity >= verbosity: if error: if no_lf: - print >>sys.stderr,msg.encode('utf-8'), + print(msg, end=' ', file=sys.stderr) else: - print >>sys.stderr,msg.encode('utf-8') + print(msg, file=sys.stderr) else: if no_lf: - print msg.encode('utf-8'), + print(msg, end=' ') else: - print msg.encode('utf-8') + print(msg) def output(self, type_, name, extra_outputs, data): if name in extra_outputs: @@ -297,7 +284,7 @@ @param output_type: True for default, else can be any registered type """ - return self._outputs[output_type].keys() + return list(self._outputs[output_type].keys()) def _make_parents(self): self.parents = {} @@ -307,11 +294,11 @@ for parent_name in ('profile', 'profile_session'): parent = self.parents[parent_name] = argparse.ArgumentParser(add_help=False) parent.add_argument("-p", "--profile", action="store", type=str, default='@DEFAULT@', help=_("Use PROFILE profile key (default: %(default)s)")) - parent.add_argument("--pwd", action="store", type=unicode_decoder, default='', metavar='PASSWORD', help=_("Password used to connect profile, if necessary")) + parent.add_argument("--pwd", action="store", default='', metavar='PASSWORD', help=_("Password used to connect profile, if necessary")) profile_parent, profile_session_parent = self.parents['profile'], self.parents['profile_session'] - connect_short, connect_long, connect_action, connect_help = "-c", "--connect", "store_true", _(u"Connect the profile before doing anything else") + connect_short, connect_long, connect_action, connect_help = "-c", "--connect", "store_true", _("Connect the profile before doing anything else") profile_parent.add_argument(connect_short, connect_long, action=connect_action, help=connect_help) profile_session_connect_group = profile_session_parent.add_mutually_exclusive_group() @@ -323,12 +310,12 @@ progress_parent.add_argument("-P", "--progress", action="store_true", help=_("Show progress bar")) verbose_parent = self.parents['verbose'] = argparse.ArgumentParser(add_help=False) - verbose_parent.add_argument('--verbose', '-v', action='count', default=0, help=_(u"Add a verbosity level (can be used multiple times)")) + verbose_parent.add_argument('--verbose', '-v', action='count', default=0, help=_("Add a verbosity level (can be used multiple times)")) draft_parent = self.parents['draft'] = argparse.ArgumentParser(add_help=False) draft_group = draft_parent.add_argument_group(_('draft handling')) - draft_group.add_argument("-D", "--current", action="store_true", help=_(u"load current draft")) - draft_group.add_argument("-F", "--draft-path", type=unicode_decoder, help=_(u"path to a draft file to retrieve")) + draft_group.add_argument("-D", "--current", action="store_true", help=_("load current draft")) + draft_group.add_argument("-F", "--draft-path", help=_("path to a draft file to retrieve")) def make_pubsub_group(self, flags, defaults): @@ -343,71 +330,71 @@ flags = misc.FlagsHandler(flags) parent = argparse.ArgumentParser(add_help=False) pubsub_group = parent.add_argument_group('pubsub') - pubsub_group.add_argument("-u", "--pubsub-url", type=unicode_decoder, - help=_(u"Pubsub URL (xmpp or http)")) + pubsub_group.add_argument("-u", "--pubsub-url", + help=_("Pubsub URL (xmpp or http)")) - service_help = _(u"JID of the PubSub service") + service_help = _("JID of the PubSub service") if not flags.service: - default = defaults.pop(u'service', _(u'PEP service')) + default = defaults.pop('service', _('PEP service')) if default is not None: - service_help += _(u" (DEFAULT: {default})".format(default=default)) - pubsub_group.add_argument("-s", "--service", type=unicode_decoder, default=u'', + service_help += _(" (DEFAULT: {default})".format(default=default)) + pubsub_group.add_argument("-s", "--service", default='', help=service_help) - node_help = _(u"node to request") + node_help = _("node to request") if not flags.node: - default = defaults.pop(u'node', _(u'standard node')) + default = defaults.pop('node', _('standard node')) if default is not None: - node_help += _(u" (DEFAULT: {default})".format(default=default)) - pubsub_group.add_argument("-n", "--node", type=unicode_decoder, default=u'', help=node_help) + node_help += _(" (DEFAULT: {default})".format(default=default)) + pubsub_group.add_argument("-n", "--node", default='', help=node_help) if flags.single_item: - item_help = (u"item to retrieve") + item_help = ("item to retrieve") if not flags.item: - default = defaults.pop(u'item', _(u'last item')) + default = defaults.pop('item', _('last item')) if default is not None: - item_help += _(u" (DEFAULT: {default})".format(default=default)) - pubsub_group.add_argument("-i", "--item", type=unicode_decoder, default=u'', + item_help += _(" (DEFAULT: {default})".format(default=default)) + pubsub_group.add_argument("-i", "--item", default='', help=item_help) - pubsub_group.add_argument("-L", "--last-item", action='store_true', help=_(u'retrieve last item')) + pubsub_group.add_argument("-L", "--last-item", action='store_true', help=_('retrieve last item')) elif flags.multi_items: # mutiple items, this activate several features: max-items, RSM, MAM # and Orbder-by - pubsub_group.add_argument("-i", "--item", type=unicode_decoder, action='append', dest='items', default=[], help=_(u"items to retrieve (DEFAULT: all)")) + pubsub_group.add_argument("-i", "--item", action='append', dest='items', default=[], help=_("items to retrieve (DEFAULT: all)")) if not flags.no_max: max_group = pubsub_group.add_mutually_exclusive_group() # XXX: defaut value for --max-items or --max is set in parse_pubsub_args max_group.add_argument( "-M", "--max-items", dest="max", type=int, - help=_(u"maximum number of items to get ({no_limit} to get all items)" + help=_("maximum number of items to get ({no_limit} to get all items)" .format(no_limit=C.NO_LIMIT))) # FIXME: it could be possible to no duplicate max (between pubsub # max-items and RSM max)should not be duplicated, RSM could be # used when available and pubsub max otherwise max_group.add_argument( "-m", "--max", dest="rsm_max", type=int, - help=_(u"maximum number of items to get per page (DEFAULT: 10)")) + help=_("maximum number of items to get per page (DEFAULT: 10)")) # RSM rsm_page_group = pubsub_group.add_mutually_exclusive_group() rsm_page_group.add_argument( - "-a", "--after", dest="rsm_after", type=unicode_decoder, - help=_(u"find page after this item"), metavar='ITEM_ID') + "-a", "--after", dest="rsm_after", + help=_("find page after this item"), metavar='ITEM_ID') rsm_page_group.add_argument( - "-b", "--before", dest="rsm_before", type=unicode_decoder, - help=_(u"find page before this item"), metavar='ITEM_ID') + "-b", "--before", dest="rsm_before", + help=_("find page before this item"), metavar='ITEM_ID') rsm_page_group.add_argument( "--index", dest="rsm_index", type=int, - help=_(u"index of the page to retrieve")) + help=_("index of the page to retrieve")) # MAM pubsub_group.add_argument( - "-f", "--filter", dest='mam_filters', type=unicode_decoder, nargs=2, - action='append', default=[], help=_(u"MAM filters to use"), - metavar=(u"FILTER_NAME", u"VALUE") + "-f", "--filter", dest='mam_filters', nargs=2, + action='append', default=[], help=_("MAM filters to use"), + metavar=("FILTER_NAME", "VALUE") ) # Order-By @@ -419,10 +406,10 @@ pubsub_group.add_argument( "-o", "--order-by", choices=[C.ORDER_BY_CREATION, C.ORDER_BY_MODIFICATION], - help=_(u"how items should be ordered")) + help=_("how items should be ordered")) if not flags.all_used: - raise exceptions.InternalError('unknown flags: {flags}'.format(flags=u', '.join(flags.unused))) + raise exceptions.InternalError('unknown flags: {flags}'.format(flags=', '.join(flags.unused))) if defaults: raise exceptions.InternalError('unused defaults: {defaults}'.format(defaults=defaults)) @@ -433,14 +420,14 @@ def register_output(self, type_, name, callback, description="", default=False): if type_ not in C.OUTPUT_TYPES: - log.error(u"Invalid output type {}".format(type_)) + log.error("Invalid output type {}".format(type_)) return self._outputs[type_][name] = {'callback': callback, 'description': description } if default: if type_ in self.default_output: - self.disp(_(u'there is already a default output for {}, ignoring new one').format(type_)) + self.disp(_('there is already a default output for {}, ignoring new one').format(type_)) else: self.default_output[type_] = name @@ -450,7 +437,7 @@ options_dict = {} for option in options: try: - key, value = option.split(u'=', 1) + key, value = option.split('=', 1) except ValueError: key, value = option, None options_dict[key.strip()] = value.strip() if value is not None else None @@ -458,8 +445,8 @@ def check_output_options(self, accepted_set, options): if not accepted_set.issuperset(options): - self.disp(u"The following output options are invalid: {invalid_options}".format( - invalid_options = u', '.join(set(options).difference(accepted_set))), + self.disp("The following output options are invalid: {invalid_options}".format( + invalid_options = ', '.join(set(options).difference(accepted_set))), error=True) self.quit(C.EXIT_BAD_ARG) @@ -478,13 +465,13 @@ module = import_module(module_path) self.import_plugin_module(module, type_) except ImportError as e: - self.disp(_(u"Can't import {module_path} plugin, ignoring it: {msg}".format( + self.disp(_("Can't import {module_path} plugin, ignoring it: {msg}".format( module_path = module_path, msg = e)), error=True) except exceptions.CancelError: continue except exceptions.MissingModule as e: - self.disp(_(u"Missing module for plugin {name}: {missing}".format( + self.disp(_("Missing module for plugin {name}: {missing}".format( name = module_path, missing = e)), error=True) @@ -498,7 +485,7 @@ try: class_names = getattr(module, '__{}__'.format(type_)) except AttributeError: - log.disp(_(u"Invalid plugin module [{type}] {module}").format(type=type_, module=module), error=True) + log.disp(_("Invalid plugin module [{type}] {module}").format(type=type_, module=module), error=True) raise ImportError else: for class_name in class_names: @@ -508,29 +495,29 @@ def get_xmpp_uri_from_http(self, http_url): """parse HTML page at http(s) URL, and looks for xmpp: uri""" if http_url.startswith('https'): - scheme = u'https' + scheme = 'https' elif http_url.startswith('http'): - scheme = u'http' + scheme = 'http' else: - raise exceptions.InternalError(u'An HTTP scheme is expected in this method') - self.disp(u"{scheme} URL found, trying to find associated xmpp: URI".format(scheme=scheme.upper()),1) + raise exceptions.InternalError('An HTTP scheme is expected in this method') + self.disp("{scheme} URL found, trying to find associated xmpp: URI".format(scheme=scheme.upper()),1) # HTTP URL, we try to find xmpp: links try: from lxml import etree except ImportError: - self.disp(u"lxml module must be installed to use http(s) scheme, please install it with \"pip install lxml\"", error=True) + self.disp("lxml module must be installed to use http(s) scheme, please install it with \"pip install lxml\"", error=True) self.quit(1) - import urllib2 + import urllib.request, urllib.error, urllib.parse parser = etree.HTMLParser() try: - root = etree.parse(urllib2.urlopen(http_url), parser) + root = etree.parse(urllib.request.urlopen(http_url), parser) except etree.XMLSyntaxError as e: - self.disp(_(u"Can't parse HTML page : {msg}").format(msg=e)) + self.disp(_("Can't parse HTML page : {msg}").format(msg=e)) links = [] else: links = root.xpath("//link[@rel='alternate' and starts-with(@href, 'xmpp:')]") if not links: - self.disp(u'Could not find alternate "xmpp:" URI, can\'t find associated XMPP PubSub node/item', error=True) + self.disp('Could not find alternate "xmpp:" URI, can\'t find associated XMPP PubSub node/item', error=True) self.quit(1) xmpp_uri = links[0].get('href') return xmpp_uri @@ -546,15 +533,15 @@ try: uri_data = uri.parseXMPPUri(url) except ValueError: - self.parser.error(_(u'invalid XMPP URL: {url}').format(url=url)) + self.parser.error(_('invalid XMPP URL: {url}').format(url=url)) else: - if uri_data[u'type'] == 'pubsub': + if uri_data['type'] == 'pubsub': # URL is alright, we only set data not already set by other options if not self.args.service: - self.args.service = uri_data[u'path'] + self.args.service = uri_data['path'] if not self.args.node: - self.args.node = uri_data[u'node'] - uri_item = uri_data.get(u'item') + self.args.node = uri_data['node'] + uri_item = uri_data.get('item') if uri_item: # there is an item in URI # we use it only if item is not already set @@ -565,7 +552,7 @@ try: items = self.args.items except AttributeError: - self.disp(_(u"item specified in URL but not needed in command, ignoring it"), error=True) + self.disp(_("item specified in URL but not needed in command, ignoring it"), error=True) else: if not items: self.args.items = [uri_item] @@ -578,22 +565,22 @@ if not item_last: self.args.item = uri_item else: - self.parser.error(_(u'XMPP URL is not a pubsub one: {url}').format(url=url)) + self.parser.error(_('XMPP URL is not a pubsub one: {url}').format(url=url)) flags = self.args._cmd._pubsub_flags # we check required arguments here instead of using add_arguments' required option # because the required argument can be set in URL if C.SERVICE in flags and not self.args.service: - self.parser.error(_(u"argument -s/--service is required")) + self.parser.error(_("argument -s/--service is required")) if C.NODE in flags and not self.args.node: - self.parser.error(_(u"argument -n/--node is required")) + self.parser.error(_("argument -n/--node is required")) if C.ITEM in flags and not self.args.item: - self.parser.error(_(u"argument -i/--item is required")) + self.parser.error(_("argument -i/--item is required")) # FIXME: mutually groups can't be nested in a group and don't support title # so we check conflict here. This may be fixed in Python 3, to be checked try: if self.args.item and self.args.item_last: - self.parser.error(_(u"--item and --item-last can't be used at the same time")) + self.parser.error(_("--item and --item-last can't be used at the same time")) except AttributeError: pass @@ -639,10 +626,10 @@ except AttributeError: pass - def confirmOrQuit(self, message, cancel_message=_(u"action cancelled by user")): + def confirmOrQuit(self, message, cancel_message=_("action cancelled by user")): """Request user to confirm action, and quit if he doesn't""" - res = raw_input("{} (y/N)? ".format(message)) + res = input("{} (y/N)? ".format(message)) if res not in ("y", "Y"): self.disp(cancel_message) self.quit(C.EXIT_USER_CANCELLED) @@ -728,11 +715,11 @@ # FIXME: need better exit codes def cant_connect(failure): - log.error(_(u"Can't connect profile: {reason}").format(reason=failure)) + log.error(_("Can't connect profile: {reason}").format(reason=failure)) self.quit(1) def cant_start_session(failure): - log.error(_(u"Can't start {profile}'s session: {reason}").format(profile=self.profile, reason=failure)) + log.error(_("Can't start {profile}'s session: {reason}").format(profile=self.profile, reason=failure)) self.quit(1) self.profile = self.bridge.profileNameGet(self.args.profile) @@ -752,7 +739,7 @@ return elif not self.bridge.profileIsSessionStarted(self.profile): if not self.args.connect: - log.error(_(u"Session for [{profile}] is not started, please start it before using jp, or use either --start-session or --connect option").format(profile=self.profile)) + log.error(_("Session for [{profile}] is not started, please start it before using jp, or use either --start-session or --connect option").format(profile=self.profile)) self.quit(1) elif not getattr(self.args, "connect", False): callback() @@ -768,7 +755,7 @@ return else: if not self.bridge.isConnected(self.profile): - log.error(_(u"Profile [{profile}] is not connected, please connect it before using jp, or use --connect option").format(profile=self.profile)) + log.error(_("Profile [{profile}] is not connected, please connect it before using jp, or use --connect option").format(profile=self.profile)) self.quit(1) callback() @@ -855,14 +842,14 @@ try: default = self.host.default_output[use_output] except KeyError: - if u'default' in choices: - default = u'default' - elif u'simple' in choices: - default = u'simple' + if 'default' in choices: + default = 'default' + elif 'simple' in choices: + default = 'simple' else: default = list(choices)[0] - output_parent.add_argument('--output', '-O', choices=sorted(choices), default=default, help=_(u"select output format (default: {})".format(default))) - output_parent.add_argument('--output-option', '--oo', type=unicode_decoder, action="append", dest='output_opts', default=[], help=_(u"output specific option")) + output_parent.add_argument('--output', '-O', choices=sorted(choices), default=default, help=_("select output format (default: {})".format(default))) + output_parent.add_argument('--output-option', '--oo', action="append", dest='output_opts', default=[], help=_("output specific option")) parents.add(output_parent) else: assert extra_outputs is None @@ -875,11 +862,11 @@ self._pubsub_flags = flags # other common options - use_opts = {k:v for k,v in kwargs.iteritems() if k.startswith('use_')} - for param, do_use in use_opts.iteritems(): + use_opts = {k:v for k,v in kwargs.items() if k.startswith('use_')} + for param, do_use in use_opts.items(): opt=param[4:] # if param is use_verbose, opt is verbose if opt not in self.host.parents: - raise exceptions.InternalError(u"Unknown parent option {}".format(opt)) + raise exceptions.InternalError("Unknown parent option {}".format(opt)) del kwargs[param] if do_use: parents.add(self.host.parents[opt]) @@ -957,12 +944,12 @@ try: size = data['size'] except KeyError: - self.disp(_(u"file size is not known, we can't show a progress bar"), 1, error=True) + self.disp(_("file size is not known, we can't show a progress bar"), 1, error=True) return False if self.host.pbar is None: #first answer, we must construct the bar self.host.pbar = progressbar.ProgressBar(max_value=int(size), - widgets=[_(u"Progress: "),progressbar.Percentage(), + widgets=[_("Progress: "),progressbar.Percentage(), " ", progressbar.Bar(), " ", @@ -986,7 +973,7 @@ can be overidden by a command @param metadata(dict): metadata as sent by bridge.progressStarted """ - self.disp(_(u"Operation started"), 2) + self.disp(_("Operation started"), 2) def onProgressUpdate(self, metadata): """Method called on each progress updata @@ -1002,14 +989,14 @@ can be overidden by a command @param metadata(dict): metadata as sent by bridge.progressFinished """ - self.disp(_(u"Operation successfully finished"), 2) + self.disp(_("Operation successfully finished"), 2) def onProgressError(self, error_msg): """Called when a progress failed @param error_msg(unicode): error message as sent by bridge.progressError """ - self.disp(_(u"Error while doing operation: {}").format(error_msg), error=True) + self.disp(_("Error while doing operation: {}").format(error_msg), error=True) def disp(self, msg, verbosity=0, error=False, no_lf=False): return self.host.disp(msg, verbosity, error, no_lf) @@ -1018,7 +1005,7 @@ try: output_type = self._output_type except AttributeError: - raise exceptions.InternalError(_(u'trying to use output when use_output has not been set')) + raise exceptions.InternalError(_('trying to use output when use_output has not been set')) return self.host.output(output_type, self.args.output, self.extra_outputs, data) def exitCb(self, msg=None): @@ -1041,7 +1028,7 @@ @param exit_code(int): shell exit code """ if msg is None: - msg = _(u"error: {}") + msg = _("error: {}") self.disp(msg.format(failure_), error=True) self.host.quit(exit_code) @@ -1054,31 +1041,31 @@ if extra is None: extra = {} else: - intersection = {C.KEY_ORDER_BY}.intersection(extra.keys()) + intersection = {C.KEY_ORDER_BY}.intersection(list(extra.keys())) if intersection: raise exceptions.ConflictError( - u"given extra dict has conflicting keys with pubsub keys " - u"{intersection}".format(intersection=intersection)) + "given extra dict has conflicting keys with pubsub keys " + "{intersection}".format(intersection=intersection)) # RSM - for attribute in (u'max', u'after', u'before', 'index'): - key = u'rsm_' + attribute + for attribute in ('max', 'after', 'before', 'index'): + key = 'rsm_' + attribute if key in extra: raise exceptions.ConflictError( - u"This key already exists in extra: u{key}".format(key=key)) + "This key already exists in extra: u{key}".format(key=key)) value = getattr(self.args, key, None) if value is not None: - extra[key] = unicode(value) + extra[key] = str(value) # MAM - if hasattr(self.args, u'mam_filters'): + if hasattr(self.args, 'mam_filters'): for key, value in self.args.mam_filters: - key = u'filter_' + key + key = 'filter_' + key if key in extra: raise exceptions.ConflictError( - u"This key already exists in extra: u{key}".format(key=key)) + "This key already exists in extra: u{key}".format(key=key)) extra[key] = value # Order-By