Philipp Hörist pushed to branch master at gajim / gajim
Commits:
1e1ced9a by Philipp Hörist at 2019-06-28T22:16:36Z
Fix detection of httpupload max-file-size
- - - - -
1 changed file:
- gajim/common/modules/httpupload.py
Changes:
=====================================
gajim/common/modules/httpupload.py
=====================================
@@ -83,12 +83,13 @@ class HTTPUpload(BaseModule):
self._log.info('Discovered component: %s', info.jid)
for form in info.dataforms:
- form_dict = form.asDict()
- if form_dict.get('FORM_TYPE') != self.httpupload_namespace:
+ form_type = form.vars.get('FORM_TYPE')
+ if (form_type is None or
+ form_type.value != self.httpupload_namespace):
continue
- size = form_dict.get('max-file-size')
+ size = form.vars.get('max-file-size')
if size is not None:
- self.max_file_size = int(size)
+ self.max_file_size = int(size.value)
break
if self.max_file_size is None:
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/1e1ced9ae712705278d1a2954ece7fd738…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/1e1ced9ae712705278d1a2954ece7fd738…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
8ce0c62c by Philipp Hörist at 2019-06-28T21:38:13Z
Remove outdated comment
- - - - -
9caa3aad by Philipp Hörist at 2019-06-28T21:47:21Z
Use nbxmpp's dataforms module
- - - - -
20301d36 by Philipp Hörist at 2019-06-28T21:47:25Z
Remove unused dataforms module
This was a copy of nbxmpp's dataforms module
- - - - -
12 changed files:
- gajim/common/caps_cache.py
- gajim/common/connection_handlers_events.py
- gajim/common/modules/adhoc_commands.py
- − gajim/common/modules/dataforms.py
- gajim/common/modules/pubsub.py
- gajim/dataforms_widget.py
- gajim/gtk/account_wizard.py
- gajim/gtk/adhoc.py
- gajim/gtk/dataform.py
- gajim/gtk/search.py
- gajim/gtk/service_registration.py
- test/gtk/dataform.py
Changes:
=====================================
gajim/common/caps_cache.py
=====================================
@@ -105,9 +105,6 @@ def create_suitable_client_caps(node, caps_hash, hash_method, fjid=None):
def compute_caps_hash(identities, features, dataforms=None, hash_method='sha-1'):
"""
Compute caps hash according to XEP-0115, V1.5
-
- dataforms are nbxmpp.DataForms objects as common.dataforms don't allow several
- values without a field type list-multi
"""
if dataforms is None:
dataforms = []
=====================================
gajim/common/connection_handlers_events.py
=====================================
@@ -21,6 +21,7 @@ import logging
import OpenSSL.crypto
import nbxmpp
+from nbxmpp.modules import dataforms
from gajim.common import nec
from gajim.common import helpers
@@ -28,7 +29,6 @@ from gajim.common import app
from gajim.common import i18n
from gajim.common.i18n import _
from gajim.common.helpers import AdditionalDataDict
-from gajim.common.modules import dataforms
from gajim.common.const import KindConstant, SSLError
from gajim.common.jingle_transport import JingleTransportSocks5
from gajim.common.file_props import FilesProp
=====================================
gajim/common/modules/adhoc_commands.py
=====================================
@@ -21,12 +21,12 @@
import nbxmpp
from nbxmpp.structs import StanzaHandler
+from nbxmpp.modules import dataforms
from gajim.common import app
from gajim.common import helpers
from gajim.common.i18n import _
from gajim.common.nec import NetworkIncomingEvent
-from gajim.common.modules import dataforms
from gajim.common.modules.base import BaseModule
=====================================
gajim/common/modules/dataforms.py deleted
=====================================
@@ -1,760 +0,0 @@
-# Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org>
-# Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
-# Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
-#
-# This file is part of Gajim.
-#
-# Gajim is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published
-# by the Free Software Foundation; version 3 only.
-#
-# Gajim is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
-
-# XEP-0004: Data Forms
-
-import nbxmpp
-
-from gajim.common import helpers
-
-
-# exceptions used in this module
-class Error(Exception):
- pass
-
-
-# when we get nbxmpp.Node which we do not understand
-class UnknownDataForm(Error):
- pass
-
-
-# when we get nbxmpp.Node which contains bad fields
-class WrongFieldValue(Error):
- pass
-
-
-# helper class to change class of already existing object
-class ExtendedNode(nbxmpp.Node):
- @classmethod
- def __new__(cls, *args, **kwargs):
- if 'extend' not in kwargs.keys() or not kwargs['extend']:
- return object.__new__(cls)
-
- extend = kwargs['extend']
- assert issubclass(cls, extend.__class__)
- extend.__class__ = cls
- return extend
-
-
-# helper to create fields from scratch
-def create_field(typ, **attrs):
- ''' Helper function to create a field of given type. '''
- field = {
- 'boolean': BooleanField,
- 'fixed': StringField,
- 'hidden': StringField,
- 'text-private': StringField,
- 'text-single': StringField,
- 'jid-multi': JidMultiField,
- 'jid-single': JidSingleField,
- 'list-multi': ListMultiField,
- 'list-single': ListSingleField,
- 'text-multi': TextMultiField,
- }[typ](typ=typ, **attrs)
- return field
-
-
-def extend_field(node):
- """
- Helper function to extend a node to field of appropriate type
- """
- # when validation (XEP-122) will go in, we could have another classes
- # like DateTimeField - so that dicts in create_field() and
- # extend_field() will be different...
- typ = node.getAttr('type')
- field = {
- 'boolean': BooleanField,
- 'fixed': StringField,
- 'hidden': StringField,
- 'text-private': StringField,
- 'text-single': StringField,
- 'jid-multi': JidMultiField,
- 'jid-single': JidSingleField,
- 'list-multi': ListMultiField,
- 'list-single': ListSingleField,
- 'text-multi': TextMultiField,
- }
- if typ not in field:
- typ = 'text-single'
- return field[typ](extend=node)
-
-
-def extend_form(node):
- """
- Helper function to extend a node to form of appropriate type
- """
- if node.getTag('reported') is not None:
- return MultipleDataForm(extend=node)
- return SimpleDataForm(extend=node)
-
-
-class DataField(ExtendedNode):
- """
- Keeps data about one field - var, field type, labels, instructions... Base
- class for different kinds of fields. Use create_field() function to construct one
- of these
- """
-
- def __init__(self, typ=None, var=None, value=None, label=None, desc=None,
- required=False, options=None, extend=None):
-
- if extend is None:
- ExtendedNode.__init__(self, 'field')
-
- self.type_ = typ
- self.var = var
- if value is not None:
- self.value = value
- if label is not None:
- self.label = label
- if desc is not None:
- self.desc = desc
- self.required = required
- self.options = options
-
- @property
- def type_(self):
- """
- Type of field. Recognized values are: 'boolean', 'fixed', 'hidden',
- 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi',
- 'text-private', 'text-single'. If you set this to something different,
- DataField will store given name, but treat all data as text-single
- """
- type_ = self.getAttr('type')
- if type_ is None:
- return 'text-single'
- return type_
-
- @type_.setter
- def type_(self, value):
- assert isinstance(value, str)
- self.setAttr('type', value)
-
- @property
- def var(self):
- """
- Field identifier
- """
- return self.getAttr('var')
-
- @var.setter
- def var(self, value):
- assert isinstance(value, str)
- self.setAttr('var', value)
-
- @var.deleter
- def var(self):
- self.delAttr('var')
-
- @property
- def label(self):
- """
- Human-readable field name
- """
- label_ = self.getAttr('label')
- if not label_:
- label_ = self.var
- return label_
-
- @label.setter
- def label(self, value):
- assert isinstance(value, str)
- self.setAttr('label', value)
-
- @label.deleter
- def label(self):
- if self.getAttr('label'):
- self.delAttr('label')
-
- @property
- def description(self):
- """
- Human-readable description of field meaning
- """
- return self.getTagData('desc') or ''
-
- @description.setter
- def description(self, value):
- assert isinstance(value, str)
- if value == '':
- del self.description
- else:
- self.setTagData('desc', value)
-
- @description.deleter
- def description(self):
- desc = self.getTag('desc')
- if desc is not None:
- self.delChild(desc)
-
- @property
- def required(self):
- """
- Controls whether this field required to fill. Boolean
- """
- return bool(self.getTag('required'))
-
- @required.setter
- def required(self, value):
- required = self.getTag('required')
- if required and not value:
- self.delChild(required)
- elif not required and value:
- self.addChild('required')
-
- @property
- def media(self):
- """
- Media data
- """
- media = self.getTag('media', namespace=nbxmpp.NS_DATA_MEDIA)
- if media:
- return Media(media)
-
- @media.setter
- def media(self, value):
- del self.media
- self.addChild(node=value)
-
- @media.deleter
- def media(self):
- media = self.getTag('media')
- if media is not None:
- self.delChild(media)
-
- @staticmethod
- def is_valid():
- return True, ''
-
-
-class Uri(nbxmpp.Node):
- def __init__(self, uri_tag):
- nbxmpp.Node.__init__(self, node=uri_tag)
-
- @property
- def type_(self):
- """
- uri type
- """
- return self.getAttr('type')
-
- @type_.setter
- def type_(self, value):
- self.setAttr('type', value)
-
- @type_.deleter
- def type_(self):
- self.delAttr('type')
-
- @property
- def uri_data(self):
- """
- uri data
- """
- return self.getData()
-
- @uri_data.setter
- def uri_data(self, value):
- self.setData(value)
-
- @uri_data.deleter
- def uri_data(self):
- self.setData(None)
-
-
-class Media(nbxmpp.Node):
- def __init__(self, media_tag):
- nbxmpp.Node.__init__(self, node=media_tag)
-
- @property
- def uris(self):
- """
- URIs of the media element.
- """
- return map(Uri, self.getTags('uri'))
-
- @uris.setter
- def uris(self, value):
- del self.uris
- for uri in value:
- self.addChild(node=uri)
-
- @uris.deleter
- def uris(self):
- for element in self.getTags('uri'):
- self.delChild(element)
-
-
-class BooleanField(DataField):
- @property
- def value(self):
- """
- Value of field. May contain True, False or None
- """
- value = self.getTagData('value')
- if value in ('0', 'false'):
- return False
- if value in ('1', 'true'):
- return True
- if value is None:
- return False # default value is False
- raise WrongFieldValue
-
- @value.setter
- def value(self, value):
- self.setTagData('value', value and '1' or '0')
-
- @value.deleter
- def value(self):
- value = self.getTag('value')
- if value is not None:
- self.delChild(value)
-
-
-class StringField(DataField):
- """
- Covers fields of types: fixed, hidden, text-private, text-single
- """
-
- @property
- def value(self):
- """
- Value of field. May be any string
- """
- return self.getTagData('value') or ''
-
- @value.setter
- def value(self, value):
- assert isinstance(value, str)
- if value == '' and not self.required:
- del self.value
- return
- self.setTagData('value', value)
-
- @value.deleter
- def value(self):
- try:
- self.delChild(self.getTag('value'))
- except ValueError: # if there already were no value tag
- pass
-
- def is_valid(self):
- if not self.required:
- return True, ''
- if not self.value:
- return False, ''
- return True, ''
-
-
-class ListField(DataField):
- """
- Covers fields of types: jid-multi, jid-single, list-multi, list-single
- """
-
- @property
- def options(self):
- """
- Options
- """
- options = []
- for element in self.getTags('option'):
- value = element.getTagData('value')
- if value is None:
- raise WrongFieldValue
- label = element.getAttr('label')
- if not label:
- label = value
- options.append((label, value))
- return options
-
- @options.setter
- def options(self, values):
- del self.options
- for value, label in values:
- self.addChild('option',
- {'label': label}).setTagData('value', value)
-
- @options.deleter
- def options(self):
- for element in self.getTags('option'):
- self.delChild(element)
-
- def iter_options(self):
- for element in self.iterTags('option'):
- value = element.getTagData('value')
- if value is None:
- raise WrongFieldValue
- label = element.getAttr('label')
- if not label:
- label = value
- yield (value, label)
-
-
-class ListSingleField(ListField, StringField):
- """
- Covers list-single field
- """
- def is_valid(self):
- if not self.required:
- return True, ''
- if not self.value:
- return False, ''
- return True, ''
-
-
-class JidSingleField(ListSingleField):
- """
- Covers jid-single fields
- """
- def is_valid(self):
- if self.value:
- try:
- helpers.parse_jid(self.value)
- return True, ''
- except Exception as error:
- return False, error
- if self.required:
- return False, ''
- return True, ''
-
-
-class ListMultiField(ListField):
- """
- Covers list-multi fields
- """
-
- @property
- def values(self):
- """
- Values held in field
- """
- values = []
- for element in self.getTags('value'):
- values.append(element.getData())
- return values
-
- @values.setter
- def values(self, values):
- del self.values
- for value in values:
- self.addChild('value').setData(value)
-
- @values.deleter
- def values(self):
- for element in self.getTags('value'):
- self.delChild(element)
-
- def iter_values(self):
- for element in self.getTags('value'):
- yield element.getData()
-
- def is_valid(self):
- if not self.required:
- return True, ''
- if not self.values:
- return False, ''
- return True, ''
-
-
-class JidMultiField(ListMultiField):
- """
- Covers jid-multi fields
- """
- def is_valid(self):
- if self.values:
- for value in self.values:
- try:
- helpers.parse_jid(value)
- except Exception as error:
- return False, error
- return True, ''
- if self.required:
- return False, ''
- return True, ''
-
-
-class TextMultiField(DataField):
- @property
- def value(self):
- """
- Value held in field
- """
- value = ''
- for element in self.iterTags('value'):
- value += '\n' + element.getData()
- return value[1:]
-
- @value.setter
- def value(self, value):
- del self.value
- if value == '':
- return
- for line in value.split('\n'):
- self.addChild('value').setData(line)
-
- @value.deleter
- def value(self):
- for element in self.getTags('value'):
- self.delChild(element)
-
- def is_valid(self):
- if not self.required:
- return True, ''
- if not self.value:
- return False, ''
- return True, ''
-
-
-class DataRecord(ExtendedNode):
- """
- The container for data fields - an xml element which has DataField elements
- as children
- """
- def __init__(self, fields=None, associated=None, extend=None):
- self.associated = associated
- self.vars = {}
- if extend is None:
- # we have to build this object from scratch
- nbxmpp.Node.__init__(self)
-
- if fields is not None:
- self.fields = fields
- else:
- # we already have nbxmpp.Node inside - try to convert all
- # fields into DataField objects
- if fields is None:
- for field in self.iterTags('field'):
- if not isinstance(field, DataField):
- extend_field(field)
- self.vars[field.var] = field
- else:
- for field in self.getTags('field'):
- self.delChild(field)
- self.fields = fields
-
- @property
- def fields(self):
- """
- List of fields in this record
- """
- return self.getTags('field')
-
- @fields.setter
- def fields(self, fields):
- del self.fields
- for field in fields:
- if not isinstance(field, DataField):
- extend_field(field)
- self.addChild(node=field)
-
- @fields.deleter
- def fields(self):
- for element in self.getTags('field'):
- self.delChild(element)
-
- def iter_fields(self):
- """
- Iterate over fields in this record. Do not take associated into account
- """
- for field in self.iterTags('field'):
- yield field
-
- def iter_with_associated(self):
- """
- Iterate over associated, yielding both our field and associated one
- together
- """
- for field in self.associated.iter_fields():
- yield self[field.var], field
-
- def __getitem__(self, item):
- return self.vars[item]
-
- def is_valid(self):
- for field in self.iter_fields():
- if not field.is_valid()[0]:
- return False
- return True
-
-
-class DataForm(ExtendedNode):
- def __init__(self, type_=None, title=None, instructions=None, extend=None):
- if extend is None:
- # we have to build form from scratch
- nbxmpp.Node.__init__(self, 'x', attrs={'xmlns': nbxmpp.NS_DATA})
-
- if type_ is not None:
- self.type_ = type_
- if title is not None:
- self.title = title
- if instructions is not None:
- self.instructions = instructions
-
- @property
- def type_(self):
- """
- Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'.
- 'form' - this form is to be filled in; you will be able soon to do:
- filledform = DataForm(replyto=thisform)
- """
- return self.getAttr('type')
-
- @type_.setter
- def type_(self, type_):
- assert type_ in ('form', 'submit', 'cancel', 'result')
- self.setAttr('type', type_)
-
- @property
- def title(self):
- """
- Title of the form
-
- Human-readable, should not contain any \\r\\n.
- """
- return self.getTagData('title')
-
- @title.setter
- def title(self, title):
- self.setTagData('title', title)
-
- @title.deleter
- def title(self):
- try:
- self.delChild('title')
- except ValueError:
- pass
-
- @property
- def instructions(self):
- """
- Instructions for this form
-
- Human-readable, may contain \\r\\n.
- """
- # TODO: the same code is in TextMultiField. join them
- value = ''
- for valuenode in self.getTags('instructions'):
- value += '\n' + valuenode.getData()
- return value[1:]
-
- @instructions.setter
- def instructions(self, value):
- del self.instructions
- if value == '':
- return
- for line in value.split('\n'):
- self.addChild('instructions').setData(line)
-
- @instructions.deleter
- def instructions(self):
- for value in self.getTags('instructions'):
- self.delChild(value)
-
- @property
- def is_reported(self):
- return self.getTag('reported') is not None
-
-
-class SimpleDataForm(DataForm, DataRecord):
- def __init__(self, type_=None, title=None, instructions=None, fields=None,
- extend=None):
- DataForm.__init__(self, type_=type_, title=title,
- instructions=instructions, extend=extend)
- DataRecord.__init__(self, fields=fields, extend=self, associated=self)
-
- def get_purged(self):
- simple_form = SimpleDataForm(extend=self)
- del simple_form.title
- simple_form.instructions = ''
- to_be_removed = []
- for field in simple_form.iter_fields():
- if field.required:
- # add <value> if there is not
- if hasattr(field, 'value') and not field.value:
- field.value = ''
- # Keep all required fields
- continue
- if ((hasattr(field, 'value') and
- not field.value and
- field.value != 0) or
- (hasattr(field, 'values') and not field.values)):
- to_be_removed.append(field)
- else:
- del field.label
- del field.description
- del field.media
- for field in to_be_removed:
- simple_form.delChild(field)
- return simple_form
-
-
-class MultipleDataForm(DataForm):
- def __init__(self, type_=None, title=None, instructions=None, items=None,
- extend=None):
- DataForm.__init__(self, type_=type_, title=title,
- instructions=instructions, extend=extend)
- # all records, recorded into DataRecords
- if extend is None:
- if items is not None:
- self.items = items
- else:
- # we already have nbxmpp.Node inside - try to convert all
- # fields into DataField objects
- if items is None:
- self.items = list(self.iterTags('item'))
- else:
- for item in self.getTags('item'):
- self.delChild(item)
- self.items = items
- reported_tag = self.getTag('reported')
- self.reported = DataRecord(extend=reported_tag)
-
- @property
- def items(self):
- """
- A list of all records
- """
- return list(self.iter_records())
-
- @items.setter
- def items(self, records):
- del self.items
- for record in records:
- if not isinstance(record, DataRecord):
- DataRecord(extend=record)
- self.addChild(node=record)
-
- @items.deleter
- def items(self):
- for record in self.getTags('item'):
- self.delChild(record)
-
- def iter_records(self):
- for record in self.getTags('item'):
- yield record
-
-#
-# @reported.setter
-# def reported(self, record):
-# try:
-# self.delChild('reported')
-# except Exception:
-# pass
-#
-# record.setName('reported')
-# self.addChild(node=record)
=====================================
gajim/common/modules/pubsub.py
=====================================
@@ -21,9 +21,9 @@
# XEP-0060: Publish-Subscribe
import nbxmpp
+from nbxmpp.modules import dataforms
from gajim.common import app
-from gajim.common.modules import dataforms
from gajim.common.nec import NetworkIncomingEvent
from gajim.common.modules.base import BaseModule
=====================================
gajim/dataforms_widget.py
=====================================
@@ -30,8 +30,9 @@ from gi.repository import GdkPixbuf
from gi.repository import GObject
from gi.repository import GLib
+from nbxmpp.modules import dataforms
+
from gajim import gtkgui_helpers
-from gajim.common.modules import dataforms
from gajim.common import helpers
from gajim.common import app
from gajim.common.i18n import _
=====================================
gajim/gtk/account_wizard.py
=====================================
@@ -18,13 +18,14 @@ from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GLib
+from nbxmpp.modules import dataforms
+
from gajim.common import app
from gajim.common import ged
from gajim.common import configpaths
from gajim.common import helpers
from gajim.common import connection
from gajim.common.i18n import _
-from gajim.common.modules import dataforms
from gajim import dataforms_widget
from gajim import gui_menu_builder
=====================================
gajim/gtk/adhoc.py
=====================================
@@ -16,14 +16,14 @@ import logging
from enum import IntEnum
from gi.repository import Gtk
+
from nbxmpp.util import is_error_result
from nbxmpp.const import AdHocAction
+from nbxmpp.modules import dataforms
from gajim.common import app
from gajim.common.i18n import _
-from gajim.common.modules import dataforms
-
from gajim.gtk.dataform import DataFormWidget
from gajim.gtk.util import ensure_not_destroyed
from gajim.gtk.util import find_widget
=====================================
gajim/gtk/dataform.py
=====================================
@@ -17,12 +17,13 @@ from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Pango
+from nbxmpp.modules.dataforms import extend_form
+
from gajim.gtkgui_helpers import scale_pixbuf_from_data
from gajim.common import app
from gajim.common.i18n import _
from gajim.common.helpers import open_uri
-from gajim.common.modules.dataforms import extend_form
from gajim.gtk.util import MultiLineLabel
from gajim.gtk.util import MaxWidthComboBoxText
=====================================
gajim/gtk/search.py
=====================================
@@ -20,12 +20,12 @@ from enum import IntEnum
from gi.repository import Gtk
+from nbxmpp.modules import dataforms
+
from gajim.common import app
from gajim.common import ged
from gajim.common.i18n import _
-from gajim.common.modules import dataforms
-
from gajim.gui_menu_builder import SearchMenu
from gajim.gtk.dataform import DataFormWidget
=====================================
gajim/gtk/service_registration.py
=====================================
@@ -17,8 +17,9 @@ from enum import IntEnum
from gi.repository import Gtk
+from nbxmpp.modules import dataforms
+
from gajim.common import app
-from gajim.common.modules import dataforms
from gajim.common.i18n import _
from gajim.gtk.dataform import DataFormWidget
=====================================
test/gtk/dataform.py
=====================================
@@ -2,9 +2,9 @@ from base64 import b64decode
from gi.repository import Gtk
import nbxmpp
+from nbxmpp.modules.dataforms import extend_form
from gajim.gtk.dataform import DataFormWidget
-from gajim.common.modules.dataforms import extend_form
from gajim.common.const import CSSPriority
from gajim.common import app
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/9c53fcbd898bd0d61e5d16f8eac586fda…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/9c53fcbd898bd0d61e5d16f8eac586fda…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / python-nbxmpp
Commits:
f9532825 by Philipp Hörist at 2019-06-28T21:36:55Z
DataForms: Detect <reported> element
- - - - -
1 changed file:
- nbxmpp/modules/dataforms.py
Changes:
=====================================
nbxmpp/modules/dataforms.py
=====================================
@@ -667,6 +667,10 @@ class DataForm(ExtendedNode):
for value in self.getTags('instructions'):
self.delChild(value)
+ @property
+ def is_reported(self):
+ return self.getTag('reported') is not None
+
class SimpleDataForm(DataForm, DataRecord):
def __init__(self, type_=None, title=None, instructions=None, fields=None,
View it on GitLab: https://dev.gajim.org/gajim/python-nbxmpp/commit/f9532825b89abf55d6d41a0b7a…
--
View it on GitLab: https://dev.gajim.org/gajim/python-nbxmpp/commit/f9532825b89abf55d6d41a0b7a…
You're receiving this email because of your account on dev.gajim.org.
Philipp Hörist pushed to branch master at gajim / gajim
Commits:
c5a8cbc3 by Daniel Brötzmann at 2019-06-25T22:13:26Z
Fix iPython View
- - - - -
d5276eae by Daniel Brötzmann at 2019-06-25T22:13:26Z
IPython: Abort if not installed
- - - - -
d0a7bc3d by Daniel Brötzmann at 2019-06-25T22:13:26Z
IPython: Fix some linting errors
- - - - -
5d812cbe by Daniel Brötzmann at 2019-06-25T22:13:26Z
IPython: Add window title
- - - - -
181027dd by Philipp Hörist at 2019-06-25T22:13:26Z
IPython: Simplify stream code
- - - - -
4757fd95 by Philipp Hörist at 2019-06-25T22:13:26Z
IPython: Support only IPython 5.0.0+
- - - - -
2 changed files:
- gajim/dev/ipython_view.py
- gajim/gui_interface.py
Changes:
=====================================
gajim/dev/ipython_view.py
=====================================
@@ -19,13 +19,29 @@
# Copyright (c) 2007, IBM Corporation
# All rights reserved.
-## Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-## * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-## * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-## * Neither the name of the IBM Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of the IBM Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
"""
Provides IPython console widget
@@ -45,6 +61,7 @@ import sys
import os
from io import StringIO
from functools import reduce
+from pkg_resources import parse_version
from gi.repository import Gtk
from gi.repository import Gdk
@@ -52,30 +69,25 @@ from gi.repository import GObject
from gi.repository import GLib
from gi.repository import Pango
-try:
- import IPython
-except ImportError:
- IPython = None
-
-HAS_IPYTHON5 = True
-try:
- from pygments.token import Token
- # pylint: disable=ungrouped-imports
- from IPython.core.displayhook import DisplayHook
- from IPython.core.display_trap import DisplayTrap
- # pylint: enable=ungrouped-imports
-
- class MyPromptDisplayHook(DisplayHook):
- def __init__(self, shell, view):
- DisplayHook.__init__(self, shell=shell)
- self.view = view
-
- def write_output_prompt(self):
- tokens = self.shell.prompts.out_prompt_tokens()
- self.view.write('\n')
- self.view.write(tokens)
-except Exception:
- HAS_IPYTHON5 = False
+import IPython
+
+from pygments.token import Token
+# pylint: disable=ungrouped-imports
+from IPython.core.displayhook import DisplayHook
+from IPython.core.display_trap import DisplayTrap
+# pylint: enable=ungrouped-imports
+
+
+class MyPromptDisplayHook(DisplayHook):
+ def __init__(self, shell, view):
+ DisplayHook.__init__(self, shell=shell)
+ self.view = view
+
+ def write_output_prompt(self):
+ tokens = self.shell.prompts.out_prompt_tokens()
+ self.view.write('\n')
+ self.view.write(tokens)
+
class IterableIPShell:
"""
@@ -117,16 +129,8 @@ class IterableIPShell:
io = IPython.utils.io
if input_func:
- if IPython.version_info[0] >= 1:
- IPython.terminal.interactiveshell.raw_input_original = input_func
- else:
- IPython.frontend.terminal.interactiveshell.raw_input_original = input_func
- if cin:
- io.stdin = io.IOStream(cin)
- if cout:
- io.stdout = io.IOStream(cout)
- if cerr:
- io.stderr = io.IOStream(cerr)
+ IPython.terminal.interactiveshell.raw_input_original = input_func
+
# This is to get rid of the blockage that accurs during
# IPython.Shell.InteractiveShell.user_setup()
@@ -135,52 +139,53 @@ class IterableIPShell:
os.environ['TERM'] = 'dumb'
excepthook = sys.excepthook
- if IPython.version_info[0] >= 5:
- from traitlets.config.loader import Config
- else:
- from IPython.config.loader import Config
+ from traitlets.config.loader import Config
cfg = Config()
cfg.InteractiveShell.colors = "Linux"
- # InteractiveShell's __init__ overwrites io.stdout,io.stderr with
- # sys.stdout, sys.stderr, this makes sure they are right
+ # InteractiveShell's __init__ gets a reference of stdout and stderr
+ # so we save the standard here to revert it after init
old_stdout, old_stderr = sys.stdout, sys.stderr
- sys.stdout, sys.stderr = io.stdout.stream, io.stderr.stream
+ sys.stdout, sys.stderr = cout, cerr
# InteractiveShell inherits from SingletonConfigurable so use instance()
- if IPython.version_info[0] >= 1:
- self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(config=cfg, user_ns=user_ns, user_module=user_global_ns)
- else:
- self.IP = IPython.frontend.terminal.embed.InteractiveShellEmbed.instance(config=cfg, user_ns=user_ns)
+ self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(
+ config=cfg, user_ns=user_ns, user_module=user_global_ns)
+ # Set back stdout and stderr to what it was before
sys.stdout, sys.stderr = old_stdout, old_stderr
self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
header='IPython system call: ',
local_ns=user_ns)
- #global_ns=user_global_ns)
- #verbose=self.IP.rc.system_verbose)
self.IP.raw_input = input_func
sys.excepthook = excepthook
self.iter_more = 0
self.history_level = 0
self.complete_sep = re.compile(r'[\s\{\}\[\]\(\)]')
- self.updateNamespace({'exit':lambda: None})
- self.updateNamespace({'quit':lambda: None})
- #self.IP.readline_startup_hook(self.IP.pre_readline)
+ self.updateNamespace({'exit': lambda: None})
+ self.updateNamespace({'quit': lambda: None})
+
# Workaround for updating namespace with sys.modules
#
self.__update_namespace()
+ # Avoid using input splitter when not really needed.
+ # Perhaps it could work even before 5.8.0
+ # But it definitely does not work any more with >= 7.0.0
+ self.no_input_splitter = parse_version(IPython.release.version) >= \
+ parse_version('5.8.0')
+ self.lines = []
+
def __update_namespace(self):
'''
Update self.IP namespace for autocompletion with sys.modules
'''
for k, v in sys.modules.items():
- if not '.' in k:
- self.IP.user_ns.update({k:v})
+ if '.' not in k:
+ self.IP.user_ns.update({k: v})
def execute(self):
"""
@@ -211,14 +216,22 @@ class IterableIPShell:
except Exception:
self.IP.showtraceback()
else:
- self.IP.input_splitter.push(line)
- self.iter_more = self.IP.input_splitter.push_accepts_more()
+ if self.no_input_splitter:
+ self.lines.append(self.IP.raw_input(self.prompt))
+ self.iter_more = self.IP.check_complete(
+ '\n'.join(self.lines))[0] == 'incomplete'
+ else:
+ self.IP.input_splitter.push(line)
+ self.iter_more = self.IP.input_splitter.push_accepts_more()
+
self.prompt = self.generatePrompt(self.iter_more)
if not self.iter_more:
- if IPython.version_info[0] >= 2:
- source_raw = self.IP.input_splitter.raw_reset()
+ if self.no_input_splitter:
+ source_raw = '\n'.join(self.lines)
+ self.lines = []
else:
- source_raw = self.IP.input_splitter.source_raw_reset()[1]
+ source_raw = self.IP.input_splitter.raw_reset()
+
self.IP.run_cell(source_raw, store_history=True)
self.IP.rl_do_indent = False
else:
@@ -240,24 +253,8 @@ class IterableIPShell:
@rtype: string
'''
- # Backwards compatibility with ipyton-0.11
- #
- ver = IPython.__version__
- if '0.11' in ver:
- prompt = self.IP.hooks.generate_prompt(is_continuation)
- else:
- # Prompt for IPython >=5.0:
- if hasattr(self.IP, 'prompts'):
- if is_continuation:
- prompt = self.IP.prompts.continuation_prompt_tokens(self.IP.prompts)
- else:
- prompt = self.IP.prompts.in_prompt_tokens(self.IP.prompts)
- # Prompt for IPython < 5.0
- elif hasattr(self.IP, 'prompt_manager'):
- if is_continuation:
- prompt = self.IP.prompt_manager.render('in2')
- else:
- prompt = self.IP.prompt_manager.render('in')
+ # TODO: Update to IPython 5.x and later
+ prompt = "In [%d]: " % self.IP.execution_count
return prompt
@@ -335,13 +332,14 @@ class IterableIPShell:
@rtype: string
'''
for i in range(len(str1)):
- if not str2.startswith(str1[:i+1]):
+ if not str2.startswith(str1[:i + 1]):
return str1[:i]
return str1
if possibilities[1]:
- common_prefix = reduce(_commonPrefix, possibilities[1]) or line[-1]
- completed = line[:-len(split_line[-1])]+common_prefix
+ common_prefix = \
+ reduce(_commonPrefix, possibilities[1]) or line[-1]
+ completed = line[:-len(split_line[-1])] + common_prefix
else:
completed = line
else:
@@ -383,9 +381,10 @@ class ConsoleView(Gtk.TextView):
self.override_font(Pango.FontDescription('Mono'))
self.set_cursor_visible(True)
self.text_buffer = self.get_buffer()
- self.mark = self.text_buffer.create_mark('scroll_mark',
- self.text_buffer.get_end_iter(),
- False)
+ self.mark = self.text_buffer.create_mark(
+ 'scroll_mark',
+ self.text_buffer.get_end_iter(),
+ False)
for code in self.ANSI_COLORS:
self.text_buffer.create_tag(code,
foreground=self.ANSI_COLORS[code],
@@ -393,22 +392,24 @@ class ConsoleView(Gtk.TextView):
self.text_buffer.create_tag('0')
self.text_buffer.create_tag('notouch', editable=False)
self.color_pat = re.compile(r'\x01?\x1b\[(.*?)m\x02?')
- if HAS_IPYTHON5:
- self.style_dict = {
- Token.Prompt: '0;32',
- Token.PromptNum: '1;32',
- Token.OutPrompt: '0;31',
- Token.OutPromptNum: '1;31',
- }
+
+ self.style_dict = {
+ Token.Prompt: '0;32',
+ Token.PromptNum: '1;32',
+ Token.OutPrompt: '0;31',
+ Token.OutPromptNum: '1;31',
+ }
+
self.line_start = \
self.text_buffer.create_mark('line_start',
- self.text_buffer.get_end_iter(), True)
+ self.text_buffer.get_end_iter(),
+ True)
self.connect('key-press-event', self.onKeyPress)
def write(self, text, editable=False):
if isinstance(text, str):
GLib.idle_add(self._write, text, editable)
- elif IPython.version_info[0] >= 5:
+ else:
GLib.idle_add(self._write5, text, editable)
def _write5(self, text, editable=False):
@@ -420,18 +421,18 @@ class ConsoleView(Gtk.TextView):
@param editable: If true, added text is editable.
@type editable: boolean
"""
- start_mark = self.text_buffer.create_mark(None,
- self.text_buffer.get_end_iter(),
- True)
+ start_mark = self.text_buffer.create_mark(
+ None, self.text_buffer.get_end_iter(), True)
for token, segment in text:
tag = self.style_dict[token]
- self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
- segment, tag)
+ self.text_buffer.insert_with_tags_by_name(
+ self.text_buffer.get_end_iter(), segment, tag)
if not editable:
- self.text_buffer.apply_tag_by_name('notouch',
- self.text_buffer.get_iter_at_mark(start_mark),
- self.text_buffer.get_end_iter())
+ self.text_buffer.apply_tag_by_name(
+ 'notouch',
+ self.text_buffer.get_iter_at_mark(start_mark),
+ self.text_buffer.get_end_iter())
self.text_buffer.delete_mark(start_mark)
self.scroll_mark_onscreen(self.mark)
@@ -444,31 +445,31 @@ class ConsoleView(Gtk.TextView):
@param editable: If true, added text is editable.
@type editable: boolean
"""
- if isinstance(text, list) and IPython.version_info[0] >= 5:
+ if isinstance(text, list):
self._write5(text, editable)
return
segments = self.color_pat.split(text)
segment = segments.pop(0)
- start_mark = self.text_buffer.create_mark(None,
- self.text_buffer.get_end_iter(),
- True)
+ start_mark = self.text_buffer.create_mark(
+ None, self.text_buffer.get_end_iter(), True)
self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)
if segments:
ansi_tags = self.color_pat.findall(text)
for tag in ansi_tags:
i = segments.index(tag)
- self.text_buffer.insert_with_tags_by_name(self.text_buffer.get_end_iter(),
- segments[i+1], str(tag))
+ self.text_buffer.insert_with_tags_by_name(
+ self.text_buffer.get_end_iter(), segments[i + 1], str(tag))
segments.pop(i)
if not editable:
- self.text_buffer.apply_tag_by_name('notouch',
- self.text_buffer.get_iter_at_mark(start_mark),
- self.text_buffer.get_end_iter())
+ self.text_buffer.apply_tag_by_name(
+ 'notouch',
+ self.text_buffer.get_iter_at_mark(start_mark),
+ self.text_buffer.get_end_iter())
+
self.text_buffer.delete_mark(start_mark)
self.scroll_mark_onscreen(self.mark)
-
def showPrompt(self, prompt):
GLib.idle_add(self._showPrompt, prompt)
@@ -495,7 +496,8 @@ class ConsoleView(Gtk.TextView):
"""
iter_ = self.text_buffer.get_iter_at_mark(self.line_start)
iter_.forward_to_line_end()
- self.text_buffer.delete(self.text_buffer.get_iter_at_mark(self.line_start), iter_)
+ self.text_buffer.delete(
+ self.text_buffer.get_iter_at_mark(self.line_start), iter_)
self._write(text, True)
def getCurrentLine(self):
@@ -526,11 +528,12 @@ class ConsoleView(Gtk.TextView):
'notouch',
self.text_buffer.get_iter_at_mark(self.line_start),
iter_)
- self._write('\n'+text)
+ self._write('\n' + text)
if text:
self._write('\n')
self._showPrompt(self.prompt)
- self.text_buffer.move_mark(self.line_start, self.text_buffer.get_end_iter())
+ self.text_buffer.move_mark(
+ self.line_start, self.text_buffer.get_end_iter())
self.text_buffer.place_cursor(self.text_buffer.get_end_iter())
if self.IP.rl_do_indent:
@@ -570,11 +573,11 @@ class ConsoleView(Gtk.TextView):
return True
elif not event.string:
pass
- elif start_iter.compare(insert_iter) <= 0 and \
- start_iter.compare(selection_iter) <= 0:
+ elif (start_iter.compare(insert_iter) <= 0 and
+ start_iter.compare(selection_iter) <= 0):
pass
- elif start_iter.compare(insert_iter) > 0 and \
- start_iter.compare(selection_iter) > 0:
+ elif (start_iter.compare(insert_iter) > 0 and
+ start_iter.compare(selection_iter) > 0):
self.text_buffer.place_cursor(start_iter)
elif insert_iter.compare(selection_iter) < 0:
self.text_buffer.move_mark(insert_mark, start_iter)
@@ -588,6 +591,7 @@ class ConsoleView(Gtk.TextView):
For some reason we can't extend onKeyPress directly (bug #500900)
"""
+
class IPythonView(ConsoleView, IterableIPShell):
'''
Sub-class of both modified IPython shell and L{ConsoleView} this makes
@@ -601,11 +605,11 @@ class IPythonView(ConsoleView, IterableIPShell):
self.cout = StringIO()
IterableIPShell.__init__(self, cout=self.cout, cerr=self.cout,
input_func=self.raw_input)
- if HAS_IPYTHON5:
- displayhook = MyPromptDisplayHook(shell=self.IP, view=self)
- self.IP.displayhook = displayhook
- self.IP.display_trap = DisplayTrap(hook=displayhook)
-# self.connect('key_press_event', self.keyPress)
+
+ displayhook = MyPromptDisplayHook(shell=self.IP, view=self)
+ self.IP.displayhook = displayhook
+ self.IP.display_trap = DisplayTrap(hook=displayhook)
+
self.interrupt = False
self.execute()
self.prompt = self.generatePrompt(False)
@@ -644,7 +648,8 @@ class IPythonView(ConsoleView, IterableIPShell):
@return: True if event should not trickle.
@rtype: boolean
"""
- if event.get_state() & Gdk.ModifierType.CONTROL_MASK and event.keyval == 99:
+ if (event.get_state() & Gdk.ModifierType.CONTROL_MASK and
+ event.keyval == 99):
self.interrupt = True
self._processLine()
return True
@@ -665,7 +670,7 @@ class IPythonView(ConsoleView, IterableIPShell):
slice_ = self.getCurrentLine()
self.write('\n')
for symbol in possibilities:
- self.write(symbol+'\n')
+ self.write(symbol + '\n')
self.showPrompt(self.prompt)
self.changeLine(completed or slice_)
return True
=====================================
gajim/gui_interface.py
=====================================
@@ -38,6 +38,7 @@ import time
import logging
from functools import partial
from threading import Thread
+from importlib.util import find_spec
from gi.repository import Gtk
from gi.repository import GLib
@@ -2074,6 +2075,13 @@ class Interface:
@staticmethod
def create_ipython_window():
+ # Check if IPython is installed
+ ipython = find_spec('IPython')
+ is_installed = ipython is not None
+ if not is_installed:
+ # Abort early to avoid tracebacks
+ print('IPython is not installed')
+ return
try:
from gajim.dev.ipython_view import IPythonView
except ImportError:
@@ -2087,6 +2095,7 @@ class Interface:
font = 'Luxi Mono 10'
window = Gtk.Window()
+ window.set_title(_('Gajim: IPython Console'))
window.set_size_request(750, 550)
window.set_resizable(True)
sw = Gtk.ScrolledWindow()
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/3ded70a5fdf6a816df47e087fccf49ce5…
--
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/3ded70a5fdf6a816df47e087fccf49ce5…
You're receiving this email because of your account on dev.gajim.org.