diff --git a/doc/_ext/docstring.py b/doc/_ext/docstring.py index d1034ffc..cf172120 100644 --- a/doc/_ext/docstring.py +++ b/doc/_ext/docstring.py @@ -4,16 +4,13 @@ def process_docstring(app, what, name, obj, options, lines): - lines[:] = [ - l.replace("@param", ":param").replace("@raise", ":raises") - for l in lines - ] + lines[:] = [l.replace("@param", ":param").replace("@raise", ":raises") for l in lines] def setup(app): app.connect("autodoc-process-docstring", process_docstring) return { - 'version': '0.1', - 'parallel_read_safe': True, - 'parallel_write_safe': True, + "version": "0.1", + "parallel_read_safe": True, + "parallel_write_safe": True, } diff --git a/doc/conf.py b/doc/conf.py index f3dfd071..831488d4 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,4 +1,3 @@ - # # Configuration file for the Sphinx documentation builder. # @@ -24,9 +23,9 @@ sys.path.insert(0, os.path.abspath("./_ext")) # -- Project information ----------------------------------------------------- -project = 'Libervia Backend' -copyright = '2019-2023 Libervia' -author = 'Libervia team' +project = "Libervia Backend" +copyright = "2019-2023 Libervia" +author = "Libervia team" version = __version__ @@ -60,22 +59,19 @@ rst_prolog = """ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ - "sphinx.ext.autodoc", - "docstring" -] +extensions = ["sphinx.ext.autodoc", "docstring"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['.templates'] +templates_path = [".templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -87,7 +83,7 @@ language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [u'.build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = [".build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = None @@ -98,7 +94,7 @@ pygments_style = None # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -109,7 +105,7 @@ html_theme = 'alabaster' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['.static'] +html_static_path = [".static"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -125,7 +121,7 @@ html_static_path = ['.static'] # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'SalutToidoc' +htmlhelp_basename = "SalutToidoc" # -- Options for LaTeX output ------------------------------------------------ @@ -134,15 +130,12 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -152,8 +145,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'SalutToi.tex', u'Libervia Documentation', - u'Libervia team', 'manual'), + (master_doc, "SalutToi.tex", "Libervia Documentation", "Libervia team", "manual"), ] @@ -161,10 +153,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'saluttoi', u'Libervia Documentation', - [author], 1) -] +man_pages = [(master_doc, "saluttoi", "Libervia Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- @@ -173,9 +162,15 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'SalutToi', u'Libervia Documentation', - author, 'SalutToi', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "SalutToi", + "Libervia Documentation", + author, + "SalutToi", + "One line description of project.", + "Miscellaneous", + ), ] @@ -194,7 +189,7 @@ epub_title = project # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # -- Extension configuration ------------------------------------------------- diff --git a/libervia/backend/__init__.py b/libervia/backend/__init__.py index 9aaa7566..ed145ad4 100644 --- a/libervia/backend/__init__.py +++ b/libervia/backend/__init__.py @@ -47,4 +47,5 @@ class Global: def storage(self) -> "Storage": return self.host.memory.storage + G = Global() diff --git a/libervia/backend/core/core_types.py b/libervia/backend/core/core_types.py index 8baaa2b8..a2cd660e 100644 --- a/libervia/backend/core/core_types.py +++ b/libervia/backend/core/core_types.py @@ -46,7 +46,9 @@ class EncryptionSession(TypedDict): # Incomplete types built through observation rather than code inspection. MessageDataExtra = TypedDict( - "MessageDataExtra", {"encrypted": bool, "origin_id": str, "headers": dict[str, Any]}, total=False + "MessageDataExtra", + {"encrypted": bool, "origin_id": str, "headers": dict[str, Any]}, + total=False, ) @@ -135,7 +137,7 @@ class SatXMPPEntity(ABC): def generate_message_xml( self, data: MessageData, - post_xml_treatments: defer.Deferred|None = None, + post_xml_treatments: defer.Deferred | None = None, ) -> MessageData: """Generate stanza from message data @@ -177,7 +179,7 @@ class SatXMPPEntity(ABC): def send(self, obj: domish.Element) -> None: # We need to call super() due to mulitple inheritance: Wokkel XMPPClient or # Component's `send` needs to be called. - super().send(obj) # type: ignore + super().send(obj) # type: ignore @abstractmethod async def send_message_data(self, mess_data: MessageData) -> MessageData: diff --git a/libervia/backend/core/xmpp.py b/libervia/backend/core/xmpp.py index 736d2d31..89d3e480 100644 --- a/libervia/backend/core/xmpp.py +++ b/libervia/backend/core/xmpp.py @@ -515,7 +515,7 @@ class SatXMPPEntity(core_types.SatXMPPEntity): xs.add_hook(C.STREAM_HOOK_SEND, hook) super(SatXMPPEntity, self)._connected(xs) - def disconnect_profile(self, reason: failure.Failure|None) -> None: + def disconnect_profile(self, reason: failure.Failure | None) -> None: if self._connected_d is not None: self.host_app.bridge.disconnected( self.profile @@ -616,11 +616,11 @@ class SatXMPPEntity(core_types.SatXMPPEntity): return iq_elt def sendError( - self, - iq_elt: domish.Element, - condition: str, - text: str|None = None, - appCondition: str|None = None + self, + iq_elt: domish.Element, + condition: str, + text: str | None = None, + appCondition: str | None = None, ) -> None: """Send error stanza build from iq_elt @@ -1355,17 +1355,19 @@ class SatMessageProtocol(xmppim.MessageProtocol): message = {} subject = {} extra = {} - data = MessageData({ - "from": from_jid, - "to": jid.JID(message_elt["to"]), - "uid": message_elt.getAttribute( - "uid", str(uuid.uuid4()) - ), # XXX: uid is not a standard attribute but may be added by plugins - "message": message, - "subject": subject, - "type": message_elt.getAttribute("type", "normal"), - "extra": extra, - }) + data = MessageData( + { + "from": from_jid, + "to": jid.JID(message_elt["to"]), + "uid": message_elt.getAttribute( + "uid", str(uuid.uuid4()) + ), # XXX: uid is not a standard attribute but may be added by plugins + "message": message, + "subject": subject, + "type": message_elt.getAttribute("type", "normal"), + "extra": extra, + } + ) try: message_id = extra["message_id"] = message_elt["id"] diff --git a/libervia/backend/memory/disco.py b/libervia/backend/memory/disco.py index ef1d3775..bbc1e5f1 100755 --- a/libervia/backend/memory/disco.py +++ b/libervia/backend/memory/disco.py @@ -207,9 +207,9 @@ class Discovery(object): def get_infos( self, client: SatXMPPEntity, - jid_: jid.JID|None=None, - node: str="", - use_cache: bool=True + jid_: jid.JID | None = None, + node: str = "", + use_cache: bool = True, ) -> defer.Deferred[disco.DiscoInfo]: """get disco infos from jid_, filling capability hash if needed diff --git a/libervia/backend/memory/encryption.py b/libervia/backend/memory/encryption.py index 1a14fef7..37bb15f4 100644 --- a/libervia/backend/memory/encryption.py +++ b/libervia/backend/memory/encryption.py @@ -82,8 +82,8 @@ class EncryptionHandler: plg_instance, name: str, namespace: str, - priority: int=0, - directed: bool=False + priority: int = 0, + directed: bool = False, ) -> None: """Register a plugin handling an encryption algorithm diff --git a/libervia/backend/memory/memory.py b/libervia/backend/memory/memory.py index d9c50372..2666922b 100644 --- a/libervia/backend/memory/memory.py +++ b/libervia/backend/memory/memory.py @@ -1672,7 +1672,7 @@ class Memory: access: dict | None = None, extra: dict | None = None, peer_jid: Any | None = None, - perms_to_check: tuple[str]|None = (C.ACCESS_PERM_WRITE,), + perms_to_check: tuple[str] | None = (C.ACCESS_PERM_WRITE,), ) -> None: """Set a file metadata. diff --git a/libervia/backend/memory/migration/versions/29f85e56b6d4_use_json_type_in_history_and_for_.py b/libervia/backend/memory/migration/versions/29f85e56b6d4_use_json_type_in_history_and_for_.py index e347b926..adcfcba1 100644 --- a/libervia/backend/memory/migration/versions/29f85e56b6d4_use_json_type_in_history_and_for_.py +++ b/libervia/backend/memory/migration/versions/29f85e56b6d4_use_json_type_in_history_and_for_.py @@ -5,52 +5,47 @@ Revises: fe3a02cb4bec Create Date: 2025-05-02 11:20:11.555189 """ + from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '29f85e56b6d4' -down_revision = 'fe3a02cb4bec' +revision = "29f85e56b6d4" +down_revision = "fe3a02cb4bec" branch_labels = None depends_on = None def upgrade(): - with op.batch_alter_table('history', schema=None) as batch_op: - batch_op.alter_column('extra', - existing_type=sa.TEXT(), - type_=sa.JSON(), - existing_nullable=True) + with op.batch_alter_table("history", schema=None) as batch_op: + batch_op.alter_column( + "extra", existing_type=sa.TEXT(), type_=sa.JSON(), existing_nullable=True + ) - with op.batch_alter_table('private_gen_bin', schema=None) as batch_op: - batch_op.alter_column('value', - existing_type=sa.TEXT(), - type_=sa.JSON(), - existing_nullable=True) + with op.batch_alter_table("private_gen_bin", schema=None) as batch_op: + batch_op.alter_column( + "value", existing_type=sa.TEXT(), type_=sa.JSON(), existing_nullable=True + ) - with op.batch_alter_table('private_ind_bin', schema=None) as batch_op: - batch_op.alter_column('value', - existing_type=sa.TEXT(), - type_=sa.JSON(), - existing_nullable=True) + with op.batch_alter_table("private_ind_bin", schema=None) as batch_op: + batch_op.alter_column( + "value", existing_type=sa.TEXT(), type_=sa.JSON(), existing_nullable=True + ) def downgrade(): - with op.batch_alter_table('private_ind_bin', schema=None) as batch_op: - batch_op.alter_column('value', - existing_type=sa.JSON(), - type_=sa.TEXT(), - existing_nullable=True) + with op.batch_alter_table("private_ind_bin", schema=None) as batch_op: + batch_op.alter_column( + "value", existing_type=sa.JSON(), type_=sa.TEXT(), existing_nullable=True + ) - with op.batch_alter_table('private_gen_bin', schema=None) as batch_op: - batch_op.alter_column('value', - existing_type=sa.JSON(), - type_=sa.TEXT(), - existing_nullable=True) + with op.batch_alter_table("private_gen_bin", schema=None) as batch_op: + batch_op.alter_column( + "value", existing_type=sa.JSON(), type_=sa.TEXT(), existing_nullable=True + ) - with op.batch_alter_table('history', schema=None) as batch_op: - batch_op.alter_column('extra', - existing_type=sa.JSON(), - type_=sa.TEXT(), - existing_nullable=True) + with op.batch_alter_table("history", schema=None) as batch_op: + batch_op.alter_column( + "extra", existing_type=sa.JSON(), type_=sa.TEXT(), existing_nullable=True + ) diff --git a/libervia/backend/memory/migration/versions/6af2d8f6be76_add_is_retroactive_to_thread_and_fix_.py b/libervia/backend/memory/migration/versions/6af2d8f6be76_add_is_retroactive_to_thread_and_fix_.py index 0ecc4df0..dc3c938f 100644 --- a/libervia/backend/memory/migration/versions/6af2d8f6be76_add_is_retroactive_to_thread_and_fix_.py +++ b/libervia/backend/memory/migration/versions/6af2d8f6be76_add_is_retroactive_to_thread_and_fix_.py @@ -5,29 +5,34 @@ Revises: 29f85e56b6d4 Create Date: 2025-05-02 11:21:54.023587 """ + from alembic import op import sqlalchemy as sa from sqlalchemy.sql import false # revision identifiers, used by Alembic. -revision = '6af2d8f6be76' -down_revision = '29f85e56b6d4' +revision = "6af2d8f6be76" +down_revision = "29f85e56b6d4" branch_labels = None depends_on = None def upgrade(): - with op.batch_alter_table('thread', schema=None) as batch_op: - batch_op.add_column(sa.Column('is_retroactive', sa.Boolean(), server_default=false(), nullable=False, comment="Indicates if thread ID was added after the original message was sent (e.g. to link a parent message which didn't have a thread ID to a thread).")) - batch_op.alter_column('thread_id', - existing_type=sa.TEXT(), - nullable=False) + with op.batch_alter_table("thread", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "is_retroactive", + sa.Boolean(), + server_default=false(), + nullable=False, + comment="Indicates if thread ID was added after the original message was sent (e.g. to link a parent message which didn't have a thread ID to a thread).", + ) + ) + batch_op.alter_column("thread_id", existing_type=sa.TEXT(), nullable=False) def downgrade(): - with op.batch_alter_table('thread', schema=None) as batch_op: - batch_op.alter_column('thread_id', - existing_type=sa.TEXT(), - nullable=True) - batch_op.drop_column('is_retroactive') + with op.batch_alter_table("thread", schema=None) as batch_op: + batch_op.alter_column("thread_id", existing_type=sa.TEXT(), nullable=True) + batch_op.drop_column("is_retroactive") diff --git a/libervia/backend/memory/migration/versions/8db042adb973_add_affiliations_access_model_and_.py b/libervia/backend/memory/migration/versions/8db042adb973_add_affiliations_access_model_and_.py index 80d6edde..f2b61072 100644 --- a/libervia/backend/memory/migration/versions/8db042adb973_add_affiliations_access_model_and_.py +++ b/libervia/backend/memory/migration/versions/8db042adb973_add_affiliations_access_model_and_.py @@ -5,36 +5,74 @@ Revises: 6af2d8f6be76 Create Date: 2025-08-02 12:03:19.726497 """ + from alembic import op import sqlalchemy as sa from libervia.backend.memory.sqla_mapping import JID # revision identifiers, used by Alembic. -revision = '8db042adb973' -down_revision = '6af2d8f6be76' +revision = "8db042adb973" +down_revision = "6af2d8f6be76" branch_labels = None depends_on = None def upgrade(): - op.create_table('pubsub_affiliations', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('node_id', sa.Integer(), nullable=False), - sa.Column('entity', JID(), nullable=False), - sa.Column('affiliation', sa.Enum('outcast', 'member', 'publisher', 'owner', name='affiliation', create_constraint=True), nullable=False), - sa.ForeignKeyConstraint(['node_id'], ['pubsub_nodes.id'], name=op.f('fk_pubsub_affiliations_node_id_pubsub_nodes'), ondelete='CASCADE'), - sa.PrimaryKeyConstraint('id', name=op.f('pk_pubsub_affiliations')), - sa.UniqueConstraint('node_id', 'entity', name=op.f('uq_pubsub_affiliations_node_id')) + op.create_table( + "pubsub_affiliations", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("node_id", sa.Integer(), nullable=False), + sa.Column("entity", JID(), nullable=False), + sa.Column( + "affiliation", + sa.Enum( + "outcast", + "member", + "publisher", + "owner", + name="affiliation", + create_constraint=True, + ), + nullable=False, + ), + sa.ForeignKeyConstraint( + ["node_id"], + ["pubsub_nodes.id"], + name=op.f("fk_pubsub_affiliations_node_id_pubsub_nodes"), + ondelete="CASCADE", + ), + sa.PrimaryKeyConstraint("id", name=op.f("pk_pubsub_affiliations")), + sa.UniqueConstraint( + "node_id", "entity", name=op.f("uq_pubsub_affiliations_node_id") + ), ) - with op.batch_alter_table('pubsub_nodes', schema=None) as batch_op: - batch_op.add_column(sa.Column('access_model', sa.Enum('open', 'whitelist', name='accessmodel', create_constraint=True), nullable=True)) - batch_op.add_column(sa.Column('publish_model', sa.Enum('publishers', 'subscribers', 'open', name='publishmodel', create_constraint=True), nullable=True)) + with op.batch_alter_table("pubsub_nodes", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "access_model", + sa.Enum("open", "whitelist", name="accessmodel", create_constraint=True), + nullable=True, + ) + ) + batch_op.add_column( + sa.Column( + "publish_model", + sa.Enum( + "publishers", + "subscribers", + "open", + name="publishmodel", + create_constraint=True, + ), + nullable=True, + ) + ) def downgrade(): - with op.batch_alter_table('pubsub_nodes', schema=None) as batch_op: - batch_op.drop_column('publish_model') - batch_op.drop_column('access_model') + with op.batch_alter_table("pubsub_nodes", schema=None) as batch_op: + batch_op.drop_column("publish_model") + batch_op.drop_column("access_model") - op.drop_table('pubsub_affiliations') + op.drop_table("pubsub_affiliations") diff --git a/libervia/backend/memory/migration/versions/ece2a6d7e4bb_add_parent_node_id_and_linked_mode_id_.py b/libervia/backend/memory/migration/versions/ece2a6d7e4bb_add_parent_node_id_and_linked_mode_id_.py index 93ca30bf..f6dfca64 100644 --- a/libervia/backend/memory/migration/versions/ece2a6d7e4bb_add_parent_node_id_and_linked_mode_id_.py +++ b/libervia/backend/memory/migration/versions/ece2a6d7e4bb_add_parent_node_id_and_linked_mode_id_.py @@ -5,28 +5,45 @@ Revises: 8db042adb973 Create Date: 2025-08-16 16:47:48.242973 """ + from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = 'ece2a6d7e4bb' -down_revision = '8db042adb973' +revision = "ece2a6d7e4bb" +down_revision = "8db042adb973" branch_labels = None depends_on = None def upgrade(): - with op.batch_alter_table('pubsub_nodes', schema=None) as batch_op: - batch_op.add_column(sa.Column('linked_node_id', sa.Integer(), nullable=True)) - batch_op.add_column(sa.Column('parent_node_id', sa.Integer(), nullable=True)) - batch_op.create_foreign_key(batch_op.f('fk_pubsub_nodes_parent_node_id_pubsub_nodes'), 'pubsub_nodes', ['parent_node_id'], ['id'], ondelete='CASCADE') - batch_op.create_foreign_key(batch_op.f('fk_pubsub_nodes_linked_node_id_pubsub_nodes'), 'pubsub_nodes', ['linked_node_id'], ['id'], ondelete='CASCADE') + with op.batch_alter_table("pubsub_nodes", schema=None) as batch_op: + batch_op.add_column(sa.Column("linked_node_id", sa.Integer(), nullable=True)) + batch_op.add_column(sa.Column("parent_node_id", sa.Integer(), nullable=True)) + batch_op.create_foreign_key( + batch_op.f("fk_pubsub_nodes_parent_node_id_pubsub_nodes"), + "pubsub_nodes", + ["parent_node_id"], + ["id"], + ondelete="CASCADE", + ) + batch_op.create_foreign_key( + batch_op.f("fk_pubsub_nodes_linked_node_id_pubsub_nodes"), + "pubsub_nodes", + ["linked_node_id"], + ["id"], + ondelete="CASCADE", + ) def downgrade(): - with op.batch_alter_table('pubsub_nodes', schema=None) as batch_op: - batch_op.drop_constraint(batch_op.f('fk_pubsub_nodes_linked_node_id_pubsub_nodes'), type_='foreignkey') - batch_op.drop_constraint(batch_op.f('fk_pubsub_nodes_parent_node_id_pubsub_nodes'), type_='foreignkey') - batch_op.drop_column('parent_node_id') - batch_op.drop_column('linked_node_id') + with op.batch_alter_table("pubsub_nodes", schema=None) as batch_op: + batch_op.drop_constraint( + batch_op.f("fk_pubsub_nodes_linked_node_id_pubsub_nodes"), type_="foreignkey" + ) + batch_op.drop_constraint( + batch_op.f("fk_pubsub_nodes_parent_node_id_pubsub_nodes"), type_="foreignkey" + ) + batch_op.drop_column("parent_node_id") + batch_op.drop_column("linked_node_id") diff --git a/libervia/backend/memory/sqla.py b/libervia/backend/memory/sqla.py index 989ceeb3..ba609f5a 100644 --- a/libervia/backend/memory/sqla.py +++ b/libervia/backend/memory/sqla.py @@ -500,7 +500,7 @@ class Storage: @aio async def get_history_from_xmpp_id( - self, client: SatXMPPEntity, message_id: str, message_type: str|None + self, client: SatXMPPEntity, message_id: str, message_type: str | None ) -> History | None: """Retrieves history row that match a specific message ID. @@ -520,12 +520,11 @@ class Storage: ) else: query = select(History).where( - History.profile_id == profile_id, History.origin_id == message_id + History.profile_id == profile_id, History.origin_id == message_id ) query = ( - query - .outerjoin(History.messages) + query.outerjoin(History.messages) .outerjoin(History.subjects) .outerjoin(History.thread) .options( @@ -540,7 +539,6 @@ class Storage: return history - @aio async def history_get( self, @@ -676,9 +674,7 @@ class Storage: # FIXME: "json_each" works with SQLite, but when PostGreSQL support will # be added, "json_array_elements" may have to be used with it. subquery = func.json_each(History.extra["keywords"]).table_valued("value") - stmt = stmt.where( - exists().where(subquery.c.value == keyword) - ) + stmt = stmt.where(exists().where(subquery.c.value == keyword)) if "origin" in filters: stmt = stmt.where( History.extra["origin"].as_string() == filters["origin"] @@ -752,7 +748,7 @@ class Storage: self, message_uid: str, thread_id: str, - parent_id: str|None, + parent_id: str | None, is_retroactive: bool, ) -> None: """Add a thread to a message. @@ -1180,8 +1176,8 @@ class Storage: with_subscriptions: bool = False, with_affiliations: bool = False, create: bool = False, - create_kwargs: dict|None = None, - ) -> PubsubNode|None: + create_kwargs: dict | None = None, + ) -> PubsubNode | None: """Retrieve a PubsubNode from DB @param service: service hosting the node @@ -1238,9 +1234,9 @@ class Storage: @aio async def get_pubsub_nodes( self, - client: SatXMPPEntity|None, - service: jid.JID|None, - node_name: str|None = None, + client: SatXMPPEntity | None, + service: jid.JID | None, + node_name: str | None = None, linked_node: bool = False, depth: int = 0, with_items: bool = False, @@ -1279,7 +1275,7 @@ class Storage: if depth > 0: options = [ selectinload(PubsubNode.child_nodes), - joinedload(PubsubNode.parent_node) + joinedload(PubsubNode.parent_node), ] else: options = [] @@ -1323,24 +1319,21 @@ class Storage: base_node_ids = [node.id for node in base_nodes] # Create recursive CTE to find child base nodes. - hierarchy = select( - PubsubNode.id, - literal(0).label('depth') - ).filter( - PubsubNode.id.in_(base_node_ids), - PubsubNode.linked_node_id.is_(None) - ).cte(name="hierarchy", recursive=True) + hierarchy = ( + select(PubsubNode.id, literal(0).label("depth")) + .filter( + PubsubNode.id.in_(base_node_ids), + PubsubNode.linked_node_id.is_(None), + ) + .cte(name="hierarchy", recursive=True) + ) # Recursive part: get direct children that are also base nodes. children = ( - select( - PubsubNode.id, - hierarchy.c.depth + 1 - ) + select(PubsubNode.id, hierarchy.c.depth + 1) .join(hierarchy, PubsubNode.parent_node_id == hierarchy.c.id) .filter( - hierarchy.c.depth < depth, - PubsubNode.linked_node_id.is_(None) + hierarchy.c.depth < depth, PubsubNode.linked_node_id.is_(None) ) ) @@ -1348,11 +1341,8 @@ class Storage: full_hierarchy = hierarchy.union_all(children) # Final query for all base nodes in the hierarchy. - hierarchy_query = ( - select(PubsubNode) - .filter(PubsubNode.id.in_( - select(full_hierarchy.c.id) - )) + hierarchy_query = select(PubsubNode).filter( + PubsubNode.id.in_(select(full_hierarchy.c.id)) ) # Apply the same eager loading options. @@ -1377,18 +1367,18 @@ class Storage: client: SatXMPPEntity, service: jid.JID, name: str, - access_model: AccessModel|None = None, - publish_model: PublishModel|None = None, + access_model: AccessModel | None = None, + publish_model: PublishModel | None = None, analyser: Optional[str] = None, type_: Optional[str] = None, subtype: Optional[str] = None, subscribed: bool = False, - extra: dict|None = None, - items: list[PubsubItem]|None = None, - affiliations: list[PubsubAffiliation]|None = None, - subscriptions: list[PubsubSub]|None = None, - parent_node: PubsubNode|None = None, - linked_node: PubsubNode|None = None + extra: dict | None = None, + items: list[PubsubItem] | None = None, + affiliations: list[PubsubAffiliation] | None = None, + subscriptions: list[PubsubSub] | None = None, + parent_node: PubsubNode | None = None, + linked_node: PubsubNode | None = None, ) -> PubsubNode: node = PubsubNode( profile_id=self.profiles[client.profile], @@ -1400,12 +1390,12 @@ class Storage: analyser=analyser, type_=type_, subtype=subtype, - extra = extra, - items = items or [], - affiliations = affiliations or [], - subscriptions = subscriptions or [], - parent_node = parent_node, - linked_node = linked_node + extra=extra, + items=items or [], + affiliations=affiliations or [], + subscriptions=subscriptions or [], + parent_node=parent_node, + linked_node=linked_node, ) async with self.session() as session: async with session.begin(): diff --git a/libervia/backend/memory/sqla_mapping.py b/libervia/backend/memory/sqla_mapping.py index 4eeb078f..e8dd682f 100644 --- a/libervia/backend/memory/sqla_mapping.py +++ b/libervia/backend/memory/sqla_mapping.py @@ -462,7 +462,7 @@ class Thread(Base): comment=( "Indicates if thread ID was added after the original message was sent (e.g. " "to link a parent message which didn't have a thread ID to a thread)." - ) + ), ) history = relationship("History", uselist=False, back_populates="thread") @@ -653,18 +653,24 @@ class PubsubNode(Base): subtype = Column(Text, nullable=True) extra = Column(JSON) - linked_node_id = Column(Integer, ForeignKey("pubsub_nodes.id", ondelete="CASCADE"), nullable=True) - parent_node_id = Column(Integer, ForeignKey("pubsub_nodes.id", ondelete="CASCADE"), nullable=True) + linked_node_id = Column( + Integer, ForeignKey("pubsub_nodes.id", ondelete="CASCADE"), nullable=True + ) + parent_node_id = Column( + Integer, ForeignKey("pubsub_nodes.id", ondelete="CASCADE"), nullable=True + ) items = relationship("PubsubItem", back_populates="node", passive_deletes=True) - affiliations = relationship("PubsubAffiliation", back_populates="node", passive_deletes=True) + affiliations = relationship( + "PubsubAffiliation", back_populates="node", passive_deletes=True + ) subscriptions = relationship("PubsubSub", back_populates="node", passive_deletes=True) linked_node = relationship( "PubsubNode", remote_side=[id], foreign_keys=[linked_node_id], - back_populates="linking_nodes" + back_populates="linking_nodes", ) linking_nodes = relationship( "PubsubNode", @@ -676,7 +682,7 @@ class PubsubNode(Base): "PubsubNode", remote_side=[id], foreign_keys=[parent_node_id], - back_populates="child_nodes" + back_populates="child_nodes", ) child_nodes = relationship( "PubsubNode", @@ -693,10 +699,10 @@ class PubsubAffiliation(Base): User by components managing a pubsub service. """ + __tablename__ = "pubsub_affiliations" __table_args__ = (UniqueConstraint("node_id", "entity"),) - id = Column(Integer, primary_key=True) node_id = Column(ForeignKey("pubsub_nodes.id", ondelete="CASCADE"), nullable=False) entity = Column(JID, nullable=False) diff --git a/libervia/backend/plugins/plugin_comp_ap_gateway/pubsub_service.py b/libervia/backend/plugins/plugin_comp_ap_gateway/pubsub_service.py index 30adaadf..d2357d03 100644 --- a/libervia/backend/plugins/plugin_comp_ap_gateway/pubsub_service.py +++ b/libervia/backend/plugins/plugin_comp_ap_gateway/pubsub_service.py @@ -286,9 +286,7 @@ class APPubsubService(rsm.PubSubService): client, requestor_actor_id, ap_account ) if "base64" not in avatar_data: - await threads.deferToThread( - self._blocking_b64encode_avatar, avatar_data - ) + await threads.deferToThread(self._blocking_b64encode_avatar, avatar_data) else: if len(itemIdentifiers) > 1: # only a single item ID is supported diff --git a/libervia/backend/plugins/plugin_comp_email_gateway/__init__.py b/libervia/backend/plugins/plugin_comp_email_gateway/__init__.py index 85c1f75e..77ab4d63 100644 --- a/libervia/backend/plugins/plugin_comp_email_gateway/__init__.py +++ b/libervia/backend/plugins/plugin_comp_email_gateway/__init__.py @@ -52,7 +52,13 @@ from libervia.backend.core.i18n import D_, _ from libervia.backend.core.log import getLogger from libervia.backend.memory.persistent import LazyPersistentBinaryDict from libervia.backend.memory.sqla import select -from libervia.backend.memory.sqla_mapping import AccessModel, Affiliation, PrivateIndBin, PublishModel, PubsubAffiliation +from libervia.backend.memory.sqla_mapping import ( + AccessModel, + Affiliation, + PrivateIndBin, + PublishModel, + PubsubAffiliation, +) from libervia.backend.models.core import MessageData from libervia.backend.plugins.plugin_comp_email_gateway.pubsub_service import ( EmailGWPubsubService, @@ -96,8 +102,17 @@ PLUGIN_INFO = { C.PI_TYPE: C.PLUG_TYPE_ENTRY_POINT, C.PI_PROTOCOLS: [], C.PI_DEPENDENCIES: [ - "XEP-0033", "XEP-0077", "XEP-0106", "XEP-0277", "XEP-0498", "XEP-0499", "GRE", "GRE-MIME", - "GRE-OpenPGP", "PUBSUB_CACHE", "TEXT_SYNTAXES" + "XEP-0033", + "XEP-0077", + "XEP-0106", + "XEP-0277", + "XEP-0498", + "XEP-0499", + "GRE", + "GRE-MIME", + "GRE-OpenPGP", + "PUBSUB_CACHE", + "TEXT_SYNTAXES", ], C.PI_RECOMMENDATIONS: [], C.PI_MAIN: "EmailGatewayComponent", @@ -276,26 +291,30 @@ class EmailGatewayComponent(GetDataHandler): assert to_email is not None subject = "This is an encrypted message." - outer = MIMEMultipart('encrypted', protocol="application/pgp-encrypted") + outer = MIMEMultipart("encrypted", protocol="application/pgp-encrypted") outer["Subject"] = subject # FIXME: use credentials here. outer["From"] = from_jid.userhost() outer["To"] = to_email - outer["Content-Type"] = "multipart/encrypted; protocol=\"application/pgp-encrypted\"" + outer["Content-Type"] = ( + 'multipart/encrypted; protocol="application/pgp-encrypted"' + ) version = MIMEApplication( "Version: 1\n", - _subtype='pgp-encrypted', - _encoder=encoders.encode_7or8bit + _subtype="pgp-encrypted", + _encoder=encoders.encode_7or8bit, ) version["Content-Description"] = "PGP/MIME version identification" encrypted_part = MIMEApplication( encrypted_payload, - _subtype='octet-stream', - _encoder=encoders.encode_7or8bit + _subtype="octet-stream", + _encoder=encoders.encode_7or8bit, ) encrypted_part["Content-Description"] = "OpenPGP encrypted message" - encrypted_part["Content-Type"] = "application/octet-stream; name=\"encrypted.asc\"" - encrypted_part["Content-Disposition"] = "inline; filename=\"encrypted.asc\"" + encrypted_part["Content-Type"] = ( + 'application/octet-stream; name="encrypted.asc"' + ) + encrypted_part["Content-Disposition"] = 'inline; filename="encrypted.asc"' outer.attach(version) outer.attach(encrypted_part) body = outer.as_bytes() @@ -497,9 +516,7 @@ class EmailGatewayComponent(GetDataHandler): msg = MIMEText(body, "plain", "UTF-8") if subject is not None: msg["Subject"] = subject - msg["From"] = formataddr( - (credentials.user_name or None, credentials.user_email) - ) + msg["From"] = formataddr((credentials.user_name or None, credentials.user_email)) if extra.addresses: assert extra.addresses.to main_to_address = extra.addresses.to[0] @@ -810,7 +827,9 @@ class EmailGatewayComponent(GetDataHandler): user_email = user_data.credentials.user_email author_name, author_email = parseaddr(email["from"]) author_email = author_email.lower() - from_jid = jid.JID(None, (self._e.escape(author_email), self.client.jid.host, None)) + from_jid = jid.JID( + None, (self._e.escape(author_email), self.client.jid.host, None) + ) # Get the email body body_mime = email.get_body(("plain",)) @@ -937,7 +956,7 @@ class EmailGatewayComponent(GetDataHandler): client = self.client.get_virtual_client(from_jid) # Now that the message is parsed, we check if it's a mailing list. - list_ids = email.get_all("list-id") + list_ids = email.get_all("list-id") if list_ids: try: await self.handle_mailing_list( @@ -950,7 +969,7 @@ class EmailGatewayComponent(GetDataHandler): user_jid, body, subject, - extra + extra, ) except exceptions.DataError as e: log.warning(f"Can't parse mailing list email: {e}.") @@ -963,6 +982,7 @@ class EmailGatewayComponent(GetDataHandler): {"": subject} if subject else None, extra=extra, ) + def parse_references(self, email: EmailMessage) -> list[str]: """Extract message IDs from the "References" header. @@ -970,7 +990,7 @@ class EmailGatewayComponent(GetDataHandler): @returns: Message IDs """ references = [] - for header in email.get_all('references', []): + for header in email.get_all("references", []): for token in header.split(): _, address = parseaddr(token) if address: @@ -984,7 +1004,7 @@ class EmailGatewayComponent(GetDataHandler): node: str, item_id: str, author_name: str, - author_email: str + author_email: str, ) -> MbData: """Convert an email to blog data. @@ -994,16 +1014,18 @@ class EmailGatewayComponent(GetDataHandler): tags = set() # Title - title = email.get('subject', '') + title = email.get("subject", "") + def strip_tags(m): value = m.group()[1:-1].strip().lower() if value: tags.add(value) return "" + title = re.sub(r"\[.+?\]", strip_tags, title).strip() # Dates - date_header = email.get('Date') + date_header = email.get("Date") if date_header: try: published = date_utils.date_parse(date_header) @@ -1023,10 +1045,10 @@ class EmailGatewayComponent(GetDataHandler): for part in email.walk(): content_type = part.get_content_type() - content_disposition = part.get('Content-Disposition', '').lower() + content_disposition = part.get("Content-Disposition", "").lower() # Skip attachments - if 'attachment' in content_disposition: + if "attachment" in content_disposition: continue payload = part.get_payload(decode=True) @@ -1034,15 +1056,15 @@ class EmailGatewayComponent(GetDataHandler): continue payload = cast(bytes, payload) - charset = part.get_content_charset() or 'utf-8' + charset = part.get_content_charset() or "utf-8" try: - payload_text = payload.decode(charset, errors='replace') + payload_text = payload.decode(charset, errors="replace") except (LookupError, TypeError): - payload_text = payload.decode('utf-8', errors='replace') + payload_text = payload.decode("utf-8", errors="replace") - if content_type == 'text/plain': + if content_type == "text/plain": plain_parts.append(payload_text) - elif content_type == 'text/html': + elif content_type == "text/html": html_parts.append(payload_text) # Prefer first valid parts for safety @@ -1055,17 +1077,17 @@ class EmailGatewayComponent(GetDataHandler): payload = email.get_payload(decode=True) if payload is not None: payload = cast(bytes, payload) - charset = email.get_content_charset() or 'utf-8' + charset = email.get_content_charset() or "utf-8" try: - content_text = payload.decode(charset, errors='replace') + content_text = payload.decode(charset, errors="replace") except (LookupError, TypeError): - content_text = payload.decode('utf-8', errors='replace') + content_text = payload.decode("utf-8", errors="replace") content_type = email.get_content_type() - if content_type == 'text/plain': + if content_type == "text/plain": content = content_text - elif content_type == 'text/html': + elif content_type == "text/html": content_xhtml = content_text if content_xhtml is None: @@ -1086,13 +1108,11 @@ class EmailGatewayComponent(GetDataHandler): content_xhtml=content_xhtml, author=author_name, author_email=author_email, - tags=list(tags) + tags=list(tags), ) async def mb_data_to_email( - self, - credentials: Credentials, - mb_data: MbData + self, credentials: Credentials, mb_data: MbData ) -> EmailMessage: """Convert blog data to an email message. @@ -1113,28 +1133,28 @@ class EmailGatewayComponent(GetDataHandler): if mb_data.title: title_parts.append(mb_data.title) - email['Subject'] = ' '.join(title_parts) + email["Subject"] = " ".join(title_parts) if tags: - email['Keywords'] = ', '.join(tags) + email["Keywords"] = ", ".join(tags) # From # FIXME: Check email according to sender. if mb_data.author and mb_data.author_email: - email['From'] = f"{mb_data.author} <{mb_data.author_email}>" + email["From"] = f"{mb_data.author} <{mb_data.author_email}>" elif mb_data.author: - email['From'] = f"{mb_data.author} <{credentials.user_email}>" + email["From"] = f"{mb_data.author} <{credentials.user_email}>" elif mb_data.author_email: - email['From'] = mb_data.author_email + email["From"] = mb_data.author_email else: - email['From'] = credentials.user_email + email["From"] = credentials.user_email # Message ID if mb_data.id: - email['Message-ID'] = f"<{mb_data.id}>" + email["Message-ID"] = f"<{mb_data.id}>" else: msg_id = make_msgid() - email['Message-ID'] = msg_id + email["Message-ID"] = msg_id mb_data.id = msg_id[1:-1] # In-Reply-To @@ -1142,20 +1162,20 @@ class EmailGatewayComponent(GetDataHandler): # Use the first reply-to reference in_reply_to = mb_data.in_reply_tos[0] if in_reply_to.ref: - email['In-Reply-To'] = f"<{in_reply_to.ref}>" + email["In-Reply-To"] = f"<{in_reply_to.ref}>" # Dates date_set = False if mb_data.published is not None: try: - email['Date'] = formatdate(mb_data.published, localtime=True) + email["Date"] = formatdate(mb_data.published, localtime=True) date_set = True except Exception: pass if not date_set and mb_data.updated is not None: try: - email['Date'] = formatdate(mb_data.updated, localtime=True) + email["Date"] = formatdate(mb_data.updated, localtime=True) except Exception: pass @@ -1172,7 +1192,7 @@ class EmailGatewayComponent(GetDataHandler): ) email.set_content(mb_data.content) - email.add_alternative(xhtml_content, subtype='html') + email.add_alternative(xhtml_content, subtype="html") elif mb_data.content: email.set_content(mb_data.content) else: @@ -1190,8 +1210,8 @@ class EmailGatewayComponent(GetDataHandler): email: EmailMessage, user_jid: jid.JID, body: str, - subject: str|None, - extra: dict + subject: str | None, + extra: dict, ) -> None: """Handle emails from mailing lists. @@ -1224,16 +1244,15 @@ class EmailGatewayComponent(GetDataHandler): create_kwargs={ "access_model": AccessModel.whitelist, "publish_model": PublishModel.publishers, - "affiliations": [PubsubAffiliation( - entity = user_jid, - affiliation = Affiliation.owner - )], - "subscribed": True + "affiliations": [ + PubsubAffiliation(entity=user_jid, affiliation=Affiliation.owner) + ], + "subscribed": True, }, ) assert root_node is not None - in_reply_to_value = ''.join(email.get_all("in-reply-to", [])) + in_reply_to_value = "".join(email.get_all("in-reply-to", [])) __, in_reply_to = parseaddr(in_reply_to_value) references = self.parse_references(email) if in_reply_to: @@ -1269,14 +1288,12 @@ class EmailGatewayComponent(GetDataHandler): Comment( uri=parent_node_uri, service=pubsub_service, - node=parent_node_name + node=parent_node_name, ) - ] + ], ) await G.storage.cache_pubsub_items( - client, - root_node, - [await empty_item_data.to_element(client)] + client, root_node, [await empty_item_data.to_element(client)] ) parent_node = await G.storage.get_pubsub_node( client, @@ -1288,11 +1305,10 @@ class EmailGatewayComponent(GetDataHandler): "access_model": AccessModel.whitelist, "publish_model": PublishModel.publishers, "subscribed": True, - "affiliations": [PubsubAffiliation( - entity = user_jid, - affiliation = Affiliation.owner - )], - "parent_node": root_node + "affiliations": [ + PubsubAffiliation(entity=user_jid, affiliation=Affiliation.owner) + ], + "parent_node": root_node, }, ) else: @@ -1300,24 +1316,19 @@ class EmailGatewayComponent(GetDataHandler): parent_node_name = cast(str, root_node.name) mb_data = self.email_to_mb_data( - email, - pubsub_service, - parent_node_name, - message_id, - author_name, - author_email + email, pubsub_service, parent_node_name, message_id, author_name, author_email ) mb_data.comments.append( Comment( - service = pubsub_service, - node = self._mb.get_comments_node(message_id), + service=pubsub_service, + node=self._mb.get_comments_node(message_id), ) ) await G.storage.cache_pubsub_items( client, parent_node, [await mb_data.to_element(client)], - [mb_data.model_dump(mode="json")] + [mb_data.model_dump(mode="json")], ) async def handle_attachment(self, part: EmailMessage, recipient_jid: jid.JID) -> None: @@ -1393,9 +1404,7 @@ class EmailGatewayComponent(GetDataHandler): partial(self.on_new_email, user_data, from_jid.userhostJID()), connected, ) - reactor.connectTCP( - credentials.imap_host, int(credentials.imap_port), factory - ) + reactor.connectTCP(credentials.imap_host, int(credentials.imap_port), factory) await connected async def _on_registration_submit( @@ -1436,9 +1445,9 @@ class EmailGatewayComponent(GetDataHandler): return True self.validate_imap_smtp_form(submit_form) - credentials = Credentials(**{ - key: field.value for key, field in submit_form.fields.items() - }) + credentials = Credentials( + **{key: field.value for key, field in submit_form.fields.items()} + ) user_data = self.users_data.get(from_jid) if user_data is None: # The user is not in cache, we cache current credentials. @@ -1460,19 +1469,14 @@ class EmailGatewayComponent(GetDataHandler): await self.storage.aset(key, credentials) return True - async def on_relayed_encryption_data( - self, - client: SatXMPPEntity, - iq_elt: domish.Element, - form: data_form.Form + self, client: SatXMPPEntity, iq_elt: domish.Element, form: data_form.Form ) -> None: from_jid = jid.JID(iq_elt["from"]).userhostJID() credentials = await self.get_credentials(from_jid) form.addField(data_form.Field(var="sender_id", value=credentials.user_email)) - @implementer(iwokkel.IDisco) class EmailGatewayHandler(XMPPHandler): @@ -1482,28 +1486,13 @@ class EmailGatewayHandler(XMPPHandler): "result", formNamespace="urn:xmpp:data-policy:0", fields=( - data_form.Field( - "list-single", - "auth_data", - "plain" - ), - data_form.Field( - "list-single", - "data-transmission", - "encrypted" - - ), - data_form.Field( - "text-single", - "encryption_algorithm", - "TLS" - - ), + data_form.Field("list-single", "auth_data", "plain"), + data_form.Field("list-single", "data-transmission", "encrypted"), + data_form.Field("text-single", "encryption_algorithm", "TLS"), data_form.Field( "text-single", "data_retention", "0", - ), ), ), @@ -1515,7 +1504,6 @@ class EmailGatewayHandler(XMPPHandler): "text-multi", "extra_info", "This gateway acts as a relay to external IMAP/SMTP servers. Data policies depend entirely on the external server chosen by the user. This gateway does not store or process user data.", - ), ), ), diff --git a/libervia/backend/plugins/plugin_comp_email_gateway/models.py b/libervia/backend/plugins/plugin_comp_email_gateway/models.py index 412309d8..46f850a5 100644 --- a/libervia/backend/plugins/plugin_comp_email_gateway/models.py +++ b/libervia/backend/plugins/plugin_comp_email_gateway/models.py @@ -22,7 +22,7 @@ from twisted.mail import imap4 class Credentials(BaseModel): - model_config = ConfigDict(extra='forbid') + model_config = ConfigDict(extra="forbid") user_name: str user_email: str smtp_host: str @@ -36,7 +36,6 @@ class Credentials(BaseModel): imap_password: str - @dataclass class UserData: credentials: Credentials diff --git a/libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py b/libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py index 820e7912..4070ea6b 100644 --- a/libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py +++ b/libervia/backend/plugins/plugin_comp_email_gateway/pubsub_service.py @@ -31,8 +31,14 @@ from libervia.backend.core.log import getLogger from libervia.backend.memory.sqla_mapping import AccessModel, Affiliation from libervia.backend.plugins.plugin_pubsub_cache import PubsubCache from libervia.backend.plugins.plugin_xep_0498 import NodeData -from libervia.backend.plugins.plugin_xep_0499 import ExtDiscoOptions, ExtDiscoResult, DiscoPubsubItem, DiscoPubsubNode +from libervia.backend.plugins.plugin_xep_0499 import ( + ExtDiscoOptions, + ExtDiscoResult, + DiscoPubsubItem, + DiscoPubsubNode, +) from libervia.backend.tools.utils import ensure_deferred + if TYPE_CHECKING: from . import EmailGatewayComponent @@ -77,10 +83,7 @@ class EmailGWPubsubResource(pubsub.PubSubResource): return defer.ensureDeferred(self.get_nodes(requestor, service, nodeIdentifier)) async def get_nodes( - self, - requestor: jid.JID, - service: jid.JID, - node_id: str + self, requestor: jid.JID, service: jid.JID, node_id: str ) -> list[str]: nodes = await G.storage.get_pubsub_nodes(self.client, self.client.jid) return [self._pfs.namespace] + [cast(str, node.name) for node in nodes] @@ -103,10 +106,8 @@ class EmailGWPubsubResource(pubsub.PubSubResource): return node_data.to_elements(), None async def items_from_mailing_list( - self, - request: rsm.PubSubRequest, - requestor_jid: jid.JID - ) -> tuple[list[domish.Element], rsm.RSMResponse|None]: + self, request: rsm.PubSubRequest, requestor_jid: jid.JID + ) -> tuple[list[domish.Element], rsm.RSMResponse | None]: """Handle items coming from mailing lists. @param request: Pubsub request. @@ -118,10 +119,7 @@ class EmailGWPubsubResource(pubsub.PubSubResource): """ node = request.nodeIdentifier node = await G.storage.get_pubsub_node( - self.client, - self.client.jid, - node, - with_affiliations=True + self.client, self.client.jid, node, with_affiliations=True ) if node is None: raise error.StanzaError("item-not-found") @@ -133,9 +131,8 @@ class EmailGWPubsubResource(pubsub.PubSubResource): for affiliation in node.affiliations: if ( affiliation.entity == requestor_jid - and affiliation.affiliation in { - Affiliation.owner, Affiliation.publisher, Affiliation.member - } + and affiliation.affiliation + in {Affiliation.owner, Affiliation.publisher, Affiliation.member} ): break else: @@ -151,7 +148,7 @@ class EmailGWPubsubResource(pubsub.PubSubResource): request.maxItems, request.itemIdentifiers, request.subscriptionIdentifier, - request.rsm + request.rsm, ) if rsm_data := metadata.get("rsm"): rsm_response = rsm.RSMResponse(**rsm_data) @@ -159,7 +156,7 @@ class EmailGWPubsubResource(pubsub.PubSubResource): rsm_response = None return ( [cast(domish.Element, ps_item.data) for ps_item in pubsub_items], - rsm_response + rsm_response, ) @ensure_deferred @@ -241,7 +238,7 @@ class EmailGWPubsubService(rsm.PubSubService): self, client: SatXMPPComponent, iq_elt: domish.Element, - ext_disco_options: ExtDiscoOptions + ext_disco_options: ExtDiscoOptions, ) -> ExtDiscoResult: """Handle pubsub extended disco""" query_elt = iq_elt.query @@ -253,25 +250,26 @@ class EmailGWPubsubService(rsm.PubSubService): nodes = await G.storage.get_pubsub_nodes( client, service=client.jid, - node_name = node, + node_name=node, linked_node=ext_disco_options.linked_nodes, depth=depth, - with_items="items" in ext_disco_options.type + with_items="items" in ext_disco_options.type, ) if depth == 0: # If depth == 0, we don't want to use `from_sqlalchemy_full_hierarchy` to # avoid returning children, and because children `parent` and `child_nodes` # fields are not loaded. - return ExtDiscoResult([ - DiscoPubsubNode.from_sqlalchemy(node, self.client.jid) - for node in nodes - ]) + return ExtDiscoResult( + [DiscoPubsubNode.from_sqlalchemy(node, self.client.jid) for node in nodes] + ) else: - return ExtDiscoResult([ - DiscoPubsubNode.from_sqlalchemy_full_hierarchy(node, self.client.jid) - for node in nodes - ]) + return ExtDiscoResult( + [ + DiscoPubsubNode.from_sqlalchemy_full_hierarchy(node, self.client.jid) + for node in nodes + ] + ) @ensure_deferred async def getDiscoInfo( diff --git a/libervia/backend/plugins/plugin_misc_groupblog.py b/libervia/backend/plugins/plugin_misc_groupblog.py index 3c2c842e..f71d3252 100644 --- a/libervia/backend/plugins/plugin_misc_groupblog.py +++ b/libervia/backend/plugins/plugin_misc_groupblog.py @@ -121,7 +121,7 @@ class GroupBlog(object): client: SatXMPPClient, mb_data: MbData, entry_elt: domish.Element, - item_elt: domish.Element + item_elt: domish.Element, ) -> None: """Build fine access permission if needed diff --git a/libervia/backend/plugins/plugin_misc_jid_search.py b/libervia/backend/plugins/plugin_misc_jid_search.py index 2636fbeb..40615cad 100644 --- a/libervia/backend/plugins/plugin_misc_jid_search.py +++ b/libervia/backend/plugins/plugin_misc_jid_search.py @@ -46,7 +46,7 @@ PLUGIN_INFO = { C.PI_DESCRIPTION: _("""Search for XMPP entities"""), } RATIO_CUTOFF = 0.6 -# Used when a search term matches a substring of a relevant data. +# Used when a search term matches a substring of a relevant data. PARTIAL_MATCH_RATIO = 0.8 MAX_CACHE_SIZE = 10 @@ -103,9 +103,7 @@ class Options(BaseModel): entities: bool = Field( default=False, description="Search for entities for direct chat" ) - groupchat: bool = Field( - default=False, description="Search for group chats." - ) + groupchat: bool = Field(default=False, description="Search for group chats.") allow_external: bool = Field( default=False, description="Authorise doing request to external services." ) @@ -221,7 +219,9 @@ class JidSearch: matches.sort( key=lambda item: ( - item.exact_match, item.relevance or 0, getattr(item, "in_roster", False) + item.exact_match, + item.relevance or 0, + getattr(item, "in_roster", False), ), reverse=True, ) diff --git a/libervia/backend/plugins/plugin_misc_text_syntaxes.py b/libervia/backend/plugins/plugin_misc_text_syntaxes.py index 0b5bad6e..14aa3b59 100644 --- a/libervia/backend/plugins/plugin_misc_text_syntaxes.py +++ b/libervia/backend/plugins/plugin_misc_text_syntaxes.py @@ -197,7 +197,7 @@ SAFE_CLASSES = { "write", # "noise" class is used by gateways (in particular email gateway) to mark annoying and # not useful content. - "noise" + "noise", } STYLES_VALUES_REGEX = ( "^(" diff --git a/libervia/backend/plugins/plugin_pubsub_cache.py b/libervia/backend/plugins/plugin_pubsub_cache.py index c6af8cbe..5a93345f 100644 --- a/libervia/backend/plugins/plugin_pubsub_cache.py +++ b/libervia/backend/plugins/plugin_pubsub_cache.py @@ -350,7 +350,7 @@ class PubsubCache: client: SatXMPPEntity, service: jid.JID, node: str, - pubsub_node: PubsubNode|None = None, + pubsub_node: PubsubNode | None = None, ) -> dict: """Use registered analysers on a node to determine what it is used for""" analyse = {"service": service, "node": node} diff --git a/libervia/backend/plugins/plugin_sec_autocrypt.py b/libervia/backend/plugins/plugin_sec_autocrypt.py index 79227453..4f50bbdd 100644 --- a/libervia/backend/plugins/plugin_sec_autocrypt.py +++ b/libervia/backend/plugins/plugin_sec_autocrypt.py @@ -232,7 +232,9 @@ class Autocrypt: post_treat.addCallback(partial(self._check_headers, client)) return True - async def add_autocrypt_header(self, client, mess_data: MessageDataLegacy) -> MessageDataLegacy: + async def add_autocrypt_header( + self, client, mess_data: MessageDataLegacy + ) -> MessageDataLegacy: to_jid = mess_data["to"] if await self.host.memory.disco.has_identity( client, "gateway", "smtp", jid.JID(to_jid.host) diff --git a/libervia/backend/plugins/plugin_sec_gre_encrypter_openpgp.py b/libervia/backend/plugins/plugin_sec_gre_encrypter_openpgp.py index b85d2bcb..e7625017 100644 --- a/libervia/backend/plugins/plugin_sec_gre_encrypter_openpgp.py +++ b/libervia/backend/plugins/plugin_sec_gre_encrypter_openpgp.py @@ -83,9 +83,7 @@ class GREEncrypterOpenPGP(Encrypter): raise exceptions.NotFound( f"No public keys found for {recipient_id!r}, we can't encrypt." ) - encrypted_data = gpg_provider.encrypt( - formatted_payload, public_keys - ) + encrypted_data = gpg_provider.encrypt(formatted_payload, public_keys) return base64.b64encode(encrypted_data).decode("ASCII") diff --git a/libervia/backend/plugins/plugin_xep_0045.py b/libervia/backend/plugins/plugin_xep_0045.py index 9eb5ca9b..75bf94fa 100644 --- a/libervia/backend/plugins/plugin_xep_0045.py +++ b/libervia/backend/plugins/plugin_xep_0045.py @@ -264,24 +264,21 @@ class XEP_0045: search_term: str, options: plugin_misc_jid_search.Options, sequence_matcher: difflib.SequenceMatcher, - matches: plugin_misc_jid_search.SearchItems + matches: plugin_misc_jid_search.SearchItems, ) -> bool: if options.groupchat: if client.muc_service is not None: # FIXME: RSM is not handled, we need proper RSM handling as number of # rooms may be large. disco_items = await self.host.memory.disco.get_items( - client, - client.muc_service + client, client.muc_service ) for disco_item in disco_items: search_item = plugin_misc_jid_search.RoomSearchItem( - entity = disco_item.entity, - name = disco_item.name or "", - local = True + entity=disco_item.entity, name=disco_item.name or "", local=True ) self._jid_search.process_matching( - search_term, sequence_matcher, matches,search_item + search_term, sequence_matcher, matches, search_item ) return True diff --git a/libervia/backend/plugins/plugin_xep_0059.py b/libervia/backend/plugins/plugin_xep_0059.py index 8a2cef4d..1afcf40d 100644 --- a/libervia/backend/plugins/plugin_xep_0059.py +++ b/libervia/backend/plugins/plugin_xep_0059.py @@ -47,12 +47,13 @@ RSM_PREFIX = "rsm_" class RSMRequest(BaseModel): """Pydantic model for RSM request parameters""" + max: int = Field(default=10, gt=0) after: str | None = None before: str | None = None index: int | None = None - @field_validator('after') + @field_validator("after") def check_after_not_empty(cls, v: str | None) -> str | None: """Validate that after value isn't empty string @@ -62,7 +63,7 @@ class RSMRequest(BaseModel): @raise ValueError: if value is an empty string """ if v == "": - raise ValueError("RSM \"after\" can't be empty") + raise ValueError('RSM "after" can\'t be empty') return v def to_wokkel_request(self) -> rsm.RSMRequest: @@ -71,10 +72,7 @@ class RSMRequest(BaseModel): @return: wokkel RSMRequest instance """ return rsm.RSMRequest( - max_=self.max, - after=self.after, - before=self.before, - index=self.index + max_=self.max, after=self.after, before=self.before, index=self.index ) @classmethod @@ -88,7 +86,7 @@ class RSMRequest(BaseModel): max=request.max, after=request.after, before=request.before, - index=request.index + index=request.index, ) def to_element(self) -> domish.Element: @@ -117,6 +115,7 @@ class RSMRequest(BaseModel): class RSMResponse(BaseModel): """Pydantic model for RSM response parameters""" + first: str | None = None last: str | None = None index: int | None = None @@ -128,10 +127,7 @@ class RSMResponse(BaseModel): @return: wokkel RSMResponse instance """ return rsm.RSMResponse( - first=self.first, - last=self.last, - index=self.index, - count=self.count + first=self.first, last=self.last, index=self.index, count=self.count ) @classmethod @@ -145,7 +141,7 @@ class RSMResponse(BaseModel): first=response.first, last=response.last, index=response.index, - count=response.count + count=response.count, ) def to_element(self) -> domish.Element: @@ -180,7 +176,7 @@ class XEP_0059: """ log.info(f"Plugin {PLUGIN_INFO[C.PI_NAME]!r} initialization.") - def get_handler(self, client) -> 'XEP_0059_handler': + def get_handler(self, client) -> "XEP_0059_handler": """Get the XMPP handler for this plugin @param client: client instance @@ -209,9 +205,7 @@ class XEP_0059: return RSMRequest(**rsm_args).to_wokkel_request() if rsm_args else None def response2dict( - self, - rsm_response: rsm.RSMResponse, - data: dict[str, str] | None = None + self, rsm_response: rsm.RSMResponse, data: dict[str, str] | None = None ) -> dict[str, str]: """Return a dict with RSM response data @@ -281,19 +275,13 @@ class XEP_0059: log.warning('Can\'t do pagination, no "last" received.') return None - return RSMRequest( - max=rsm_request.max, - after=rsm_response.last - ) + return RSMRequest(max=rsm_request.max, after=rsm_response.last) @implementer(iwokkel.IDisco) class XEP_0059_handler(xmlstream.XMPPHandler): def getDiscoInfo( - self, - requestor: str, - target: str, - nodeIdentifier: str = "" + self, requestor: str, target: str, nodeIdentifier: str = "" ) -> list[disco.DiscoFeature]: """Get disco info for RSM @@ -305,10 +293,7 @@ class XEP_0059_handler(xmlstream.XMPPHandler): return [disco.DiscoFeature(rsm.NS_RSM)] def getDiscoItems( - self, - requestor: str, - target: str, - nodeIdentifier: str = "" + self, requestor: str, target: str, nodeIdentifier: str = "" ) -> list: """Get disco items for RSM diff --git a/libervia/backend/plugins/plugin_xep_0060.py b/libervia/backend/plugins/plugin_xep_0060.py index 88d81594..c4af6f8b 100644 --- a/libervia/backend/plugins/plugin_xep_0060.py +++ b/libervia/backend/plugins/plugin_xep_0060.py @@ -19,7 +19,18 @@ from collections import namedtuple from functools import reduce -from typing import Any, Callable, ClassVar, Dict, Iterable, List, Optional, Self, Set, Union +from typing import ( + Any, + Callable, + ClassVar, + Dict, + Iterable, + List, + Optional, + Self, + Set, + Union, +) import urllib.parse from pydantic import BaseModel, Field @@ -79,9 +90,8 @@ DEFAULT_PUBSUB_MIN_FEAT = { class NodeMetadata(BaseModel): """Node metadata model.""" - model_config = { - "extra": "allow" - } + + model_config = {"extra": "allow"} type: str | None = None creator: JIDType | None = None @@ -199,9 +209,7 @@ class NodeMetadata(BaseModel): "pubsub#owner": self.owner, "pubsub#publisher": self.publisher, "pubsub#num_subscribers": ( - str(self.num_subscribers) - if self.num_subscribers is not None - else None + str(self.num_subscribers) if self.num_subscribers is not None else None ), } form.makeFields( diff --git a/libervia/backend/plugins/plugin_xep_0277.py b/libervia/backend/plugins/plugin_xep_0277.py index f554ca59..a6f1ad8a 100644 --- a/libervia/backend/plugins/plugin_xep_0277.py +++ b/libervia/backend/plugins/plugin_xep_0277.py @@ -90,20 +90,20 @@ class Comment(BaseModel): service: JIDType | None = None node: str = "" - @model_validator(mode='after') + @model_validator(mode="after") def set_uri_if_missing(self) -> Self: if not self.uri and self.service is not None and self.node: self.uri = xmpp_uri.build_xmpp_uri( - "pubsub", - path=self.service.full(), - node=self.node + "pubsub", path=self.service.full(), node=self.node ) return self + class InReplyTo(BaseModel): ref: str - href: str|None - type: str|None = None + href: str | None + type: str | None = None + class Attachment(BaseModel): sources: list[dict[str, str]] = Field(default_factory=list) @@ -157,7 +157,6 @@ class MbData(BaseModel): updated: float | None = None language: str | None = None - title: str | None = None title_xhtml: str | None = None title_rich: str | None = Field(default=None, exclude=True) @@ -172,7 +171,7 @@ class MbData(BaseModel): allow_comments: bool | None = None comments: list[Comment] = Field(default_factory=list) - in_reply_tos: list[InReplyTo]|None = None + in_reply_tos: list[InReplyTo] | None = None tags: list[str] = Field(default_factory=list) extra: MbExtra = Field(default_factory=MbExtra) @@ -365,7 +364,7 @@ class MbData(BaseModel): in_reply_to = InReplyTo( ref=in_reply_to_elt.getAttribute("ref", ""), href=in_reply_to_elt.getAttribute("href"), - type=in_reply_to_elt.getAttribute("type") + type=in_reply_to_elt.getAttribute("type"), ) if not in_reply_to_elt.ref: log.warning( @@ -722,7 +721,7 @@ class MbData(BaseModel): if href := in_reply_to.href is not None: in_reply_to_elt["href"] = href if type_ := in_reply_to.type is not None: - in_reply_to_elt["type"] = type_ + in_reply_to_elt["type"] = type_ ## Reposts ## if self.extra.repeated: diff --git a/libervia/backend/plugins/plugin_xep_0297.py b/libervia/backend/plugins/plugin_xep_0297.py index b4ec1a18..93b8a839 100644 --- a/libervia/backend/plugins/plugin_xep_0297.py +++ b/libervia/backend/plugins/plugin_xep_0297.py @@ -41,11 +41,9 @@ from libervia.backend.plugins.plugin_xep_0428 import XEP_0428 from libervia.backend.tools.common.date_utils import date_fmt - log = getLogger(__name__) - PLUGIN_INFO = { C.PI_NAME: "Stanza Forwarding", C.PI_IMPORT_NAME: "XEP-0297", @@ -97,32 +95,27 @@ class XEP_0297: XEP_0297.update_uri(child, uri) def _forward( - self, - message_id: str, - recipient_jid_s: str, - profile_key: str + self, message_id: str, recipient_jid_s: str, profile_key: str ) -> defer.Deferred[None]: client = self.host.get_client(profile_key) recipient_jid = jid.JID(recipient_jid_s) return defer.ensureDeferred(self.forward_by_id(client, message_id, recipient_jid)) async def forward_by_id( - self, - client: SatXMPPEntity, - message_id: str, - recipient_jid: jid.JID + self, client: SatXMPPEntity, message_id: str, recipient_jid: jid.JID ) -> None: - history = cast(History, await G.storage.get( - client, + history = cast( History, - History.uid, - message_id, - joined_loads=[History.messages, History.subjects, History.thread], - )) + await G.storage.get( + client, + History, + History.uid, + message_id, + joined_loads=[History.messages, History.subjects, History.thread], + ), + ) if not history: - raise exceptions.NotFound( - f"No history found with message {message_id!r}." - ) + raise exceptions.NotFound(f"No history found with message {message_id!r}.") # FIXME: Q&D way to get MessageData from History. History should have a proper # way to do that. serialised_history = history.serialise() @@ -138,7 +131,7 @@ class XEP_0297: f"{client.jid.userhost()} is forwarding this message to you:", f"[{date_fmt(timestamp_float)}]", f"From: {history.source_jid.full()}", - f"To: {history.dest_jid.full()}" + f"To: {history.dest_jid.full()}", ] for subject in history.subjects: @@ -162,8 +155,8 @@ class XEP_0297: client: SatXMPPEntity, stanza: domish.Element, to_jid: jid.JID, - timestamp: datetime|None, - fallback_msg: str | None = None + timestamp: datetime | None, + fallback_msg: str | None = None, ): """Forward a message to the given JID. diff --git a/libervia/backend/plugins/plugin_xep_0373.py b/libervia/backend/plugins/plugin_xep_0373.py index 25b8b423..d19a22dc 100644 --- a/libervia/backend/plugins/plugin_xep_0373.py +++ b/libervia/backend/plugins/plugin_xep_0373.py @@ -120,8 +120,8 @@ def crc24(data: bytes) -> int: def binary_to_ascii_armor( data: bytes, armor_type: str = "MESSAGE", - headers: dict|None = None, - line_length: int = 76 + headers: dict | None = None, + line_length: int = 76, ) -> str: """Convert binary data to OpenPGP ASCII Armor format. @@ -131,14 +131,15 @@ def binary_to_ascii_armor( @param line_length: Maximum length of each line. @return: The ASCII Armor encoded string. """ - encoded_data = base64.b64encode(data).decode('ascii') + encoded_data = base64.b64encode(data).decode("ascii") encoded_lines = [ - encoded_data[i:i+line_length] for i in range(0, len(encoded_data), line_length) + encoded_data[i : i + line_length] + for i in range(0, len(encoded_data), line_length) ] checksum = crc24(data) - checksum_str = base64.b64encode(checksum.to_bytes(3, 'big')).decode('ascii') + checksum_str = base64.b64encode(checksum.to_bytes(3, "big")).decode("ascii") header_lines = [] if headers: @@ -153,7 +154,7 @@ def binary_to_ascii_armor( armor.append(f"={checksum_str}") armor.append(f"-----END PGP {armor_type}-----") - return '\n'.join(armor) + return "\n".join(armor) class VerificationError(Exception): diff --git a/libervia/backend/plugins/plugin_xep_0376.py b/libervia/backend/plugins/plugin_xep_0376.py index f0ff59a6..2cb0c51f 100644 --- a/libervia/backend/plugins/plugin_xep_0376.py +++ b/libervia/backend/plugins/plugin_xep_0376.py @@ -142,7 +142,7 @@ class XEP_0376: self, client: SatXMPPEntity, service: Optional[jid.JID], - node: str|None, + node: str | None, ) -> Tuple[bool, Optional[List[Dict[str, Any]]]]: if not await self.host.has_feature(client, NS_PAM): return True, None diff --git a/libervia/backend/plugins/plugin_xep_0428.py b/libervia/backend/plugins/plugin_xep_0428.py index 54352d3c..88f3f949 100644 --- a/libervia/backend/plugins/plugin_xep_0428.py +++ b/libervia/backend/plugins/plugin_xep_0428.py @@ -50,10 +50,7 @@ class XEP_0428: host.register_namespace("fallback", NS_FALLBACK) def add_fallback_elt( - self, - message_elt: domish.Element, - for_: str, - msg: str|None = None + self, message_elt: domish.Element, for_: str, msg: str | None = None ) -> None: """Add the fallback indication element diff --git a/libervia/backend/plugins/plugin_xep_0461.py b/libervia/backend/plugins/plugin_xep_0461.py index 7aec7a2a..3c954b00 100644 --- a/libervia/backend/plugins/plugin_xep_0461.py +++ b/libervia/backend/plugins/plugin_xep_0461.py @@ -63,7 +63,7 @@ class ReplyTo(BaseModel): internal_uid: bool = Field( default=True, description="True if the id is a message UID, i.e. Libervia internal ID of " - "message, otherwise it's an XMPP stanza ID or origin ID as specified in the XEP" + "message, otherwise it's an XMPP stanza ID or origin ID as specified in the XEP", ) async def ensure_xmpp_id(self, client: SatXMPPEntity) -> None: @@ -118,7 +118,8 @@ class ReplyTo(BaseModel): if self.internal_uid: raise exceptions.DataError( '"id" must be converted to XMPP id before calling "to_element". Please ' - '"use ensure_xmpp_id" first.') + '"use ensure_xmpp_id" first.' + ) reply_elt = domish.Element((NS_REPLY, "reply"), attribs={"id": self.id}) if self.to is not None: reply_elt["to"] = self.to.full() @@ -188,7 +189,9 @@ class XEP_0461: return True @ensure_deferred - async def _add_reply_to_elt(self, mess_data: MessageData, client: SatXMPPEntity) -> MessageData: + async def _add_reply_to_elt( + self, mess_data: MessageData, client: SatXMPPEntity + ) -> MessageData: try: # FIXME: Once MessageData is fully moved to Pydantic, we should have directly # the ReplyTo instance here. diff --git a/libervia/backend/test/helpers_plugins.py b/libervia/backend/test/helpers_plugins.py index ce039f94..0ec643a2 100644 --- a/libervia/backend/test/helpers_plugins.py +++ b/libervia/backend/test/helpers_plugins.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Helpers class for plugin dependencies """ +"""Helpers class for plugin dependencies""" from twisted.internet import defer diff --git a/libervia/backend/test/test_helpers_plugins.py b/libervia/backend/test/test_helpers_plugins.py index 754ea45f..a019d764 100644 --- a/libervia/backend/test/test_helpers_plugins.py +++ b/libervia/backend/test/test_helpers_plugins.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Test the helper classes to see if they behave well""" +"""Test the helper classes to see if they behave well""" from libervia.backend.test import helpers from libervia.backend.test import helpers_plugins diff --git a/libervia/backend/test/test_memory_crypto.py b/libervia/backend/test/test_memory_crypto.py index 9e306a67..76e9870c 100644 --- a/libervia/backend/test/test_memory_crypto.py +++ b/libervia/backend/test/test_memory_crypto.py @@ -19,7 +19,7 @@ # along with this program. If not, see . -""" Tests for the plugin radiocol """ +"""Tests for the plugin radiocol""" from libervia.backend.test import helpers from libervia.backend.memory.crypto import BlockCipher, PasswordHasher diff --git a/libervia/backend/test/test_plugin_misc_groupblog.py b/libervia/backend/test/test_plugin_misc_groupblog.py index 8bcde755..89d73fcc 100644 --- a/libervia/backend/test/test_plugin_misc_groupblog.py +++ b/libervia/backend/test/test_plugin_misc_groupblog.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin groupblogs """ +"""Plugin groupblogs""" from .constants import Const as C from libervia.backend.test import helpers, helpers_plugins diff --git a/libervia/backend/test/test_plugin_misc_radiocol.py b/libervia/backend/test/test_plugin_misc_radiocol.py index b053bf07..76fe4621 100644 --- a/libervia/backend/test/test_plugin_misc_radiocol.py +++ b/libervia/backend/test/test_plugin_misc_radiocol.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Tests for the plugin radiocol """ +"""Tests for the plugin radiocol""" from libervia.backend.core import exceptions from libervia.backend.test import helpers, helpers_plugins diff --git a/libervia/backend/test/test_plugin_misc_room_game.py b/libervia/backend/test/test_plugin_misc_room_game.py index 3db9e279..2b43f6aa 100644 --- a/libervia/backend/test/test_plugin_misc_room_game.py +++ b/libervia/backend/test/test_plugin_misc_room_game.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Tests for the plugin room game (base class for MUC games) """ +"""Tests for the plugin room game (base class for MUC games)""" from libervia.backend.core.i18n import _ from .constants import Const diff --git a/libervia/backend/test/test_plugin_misc_text_syntaxes.py b/libervia/backend/test/test_plugin_misc_text_syntaxes.py index 656a86cc..c04c7a61 100644 --- a/libervia/backend/test/test_plugin_misc_text_syntaxes.py +++ b/libervia/backend/test/test_plugin_misc_text_syntaxes.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin text syntaxes tests """ +"""Plugin text syntaxes tests""" from libervia.backend.test import helpers from libervia.backend.plugins import plugin_misc_text_syntaxes diff --git a/libervia/backend/test/test_plugin_xep_0085.py b/libervia/backend/test/test_plugin_xep_0085.py index 32356294..f87ccb19 100644 --- a/libervia/backend/test/test_plugin_xep_0085.py +++ b/libervia/backend/test/test_plugin_xep_0085.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin chat states notification tests """ +"""Plugin chat states notification tests""" from .constants import Const from libervia.backend.test import helpers diff --git a/libervia/backend/test/test_plugin_xep_0203.py b/libervia/backend/test/test_plugin_xep_0203.py index 1493dc06..6b5cd3bb 100644 --- a/libervia/backend/test/test_plugin_xep_0203.py +++ b/libervia/backend/test/test_plugin_xep_0203.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin XEP-0203 """ +"""Plugin XEP-0203""" from libervia.backend.test import helpers from libervia.backend.plugins.plugin_xep_0203 import XEP_0203 diff --git a/libervia/backend/test/test_plugin_xep_0277.py b/libervia/backend/test/test_plugin_xep_0277.py index 45619a31..730dcc4b 100644 --- a/libervia/backend/test/test_plugin_xep_0277.py +++ b/libervia/backend/test/test_plugin_xep_0277.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin XEP-0277 tests """ +"""Plugin XEP-0277 tests""" from libervia.backend.test import helpers from libervia.backend.plugins import plugin_xep_0277 diff --git a/libervia/backend/test/test_plugin_xep_0297.py b/libervia/backend/test/test_plugin_xep_0297.py index 30d33c99..c54cd8bb 100644 --- a/libervia/backend/test/test_plugin_xep_0297.py +++ b/libervia/backend/test/test_plugin_xep_0297.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin XEP-0297 """ +"""Plugin XEP-0297""" from .constants import Const as C from libervia.backend.test import helpers diff --git a/libervia/backend/test/test_plugin_xep_0313.py b/libervia/backend/test/test_plugin_xep_0313.py index 140558eb..212d2c75 100644 --- a/libervia/backend/test/test_plugin_xep_0313.py +++ b/libervia/backend/test/test_plugin_xep_0313.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin XEP-0313 """ +"""Plugin XEP-0313""" from .constants import Const as C from libervia.backend.test import helpers diff --git a/libervia/backend/test/test_plugin_xep_0334.py b/libervia/backend/test/test_plugin_xep_0334.py index bb6249a9..3205019b 100644 --- a/libervia/backend/test/test_plugin_xep_0334.py +++ b/libervia/backend/test/test_plugin_xep_0334.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Plugin XEP-0334 """ +"""Plugin XEP-0334""" from .constants import Const as C from libervia.backend.test import helpers diff --git a/libervia/backend/tools/common/data_format.py b/libervia/backend/tools/common/data_format.py index edd57f71..19b16a01 100644 --- a/libervia/backend/tools/common/data_format.py +++ b/libervia/backend/tools/common/data_format.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" tools common to backend and frontends """ +"""tools common to backend and frontends""" #  FIXME: json may be more appropriate than manual serialising like done here from typing import Any diff --git a/libervia/backend/tools/common/date_utils.py b/libervia/backend/tools/common/date_utils.py index cfbfcc17..bee595be 100644 --- a/libervia/backend/tools/common/date_utils.py +++ b/libervia/backend/tools/common/date_utils.py @@ -61,7 +61,7 @@ TZ_UTC = tz.tzutc() TZ_LOCAL = tz.gettz() -def date_parse(value: str, default_tz: datetime.tzinfo=TZ_UTC) -> int: +def date_parse(value: str, default_tz: datetime.tzinfo = TZ_UTC) -> int: """Parse a date and return corresponding unix timestamp @param value: date to parse, in any format supported by parser diff --git a/libervia/backend/tools/common/dynamic_import.py b/libervia/backend/tools/common/dynamic_import.py index 68cc50bc..45a85b59 100644 --- a/libervia/backend/tools/common/dynamic_import.py +++ b/libervia/backend/tools/common/dynamic_import.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" tools dynamic import """ +"""tools dynamic import""" from importlib import import_module from libervia.backend.core.log import getLogger diff --git a/libervia/backend/tools/common/regex.py b/libervia/backend/tools/common/regex.py index e2d703d1..dd000af8 100644 --- a/libervia/backend/tools/common/regex.py +++ b/libervia/backend/tools/common/regex.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" regex tools common to backend and frontends """ +"""regex tools common to backend and frontends""" import re import unicodedata diff --git a/libervia/backend/tools/common/template_xmlui.py b/libervia/backend/tools/common/template_xmlui.py index da52c8fa..81ec3162 100644 --- a/libervia/backend/tools/common/template_xmlui.py +++ b/libervia/backend/tools/common/template_xmlui.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" template XMLUI parsing +"""template XMLUI parsing XMLUI classes from this modules can then be iterated to create the template """ diff --git a/libervia/backend/tools/common/uri.py b/libervia/backend/tools/common/uri.py index e7c39423..c5c9d8bc 100644 --- a/libervia/backend/tools/common/uri.py +++ b/libervia/backend/tools/common/uri.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" XMPP uri parsing tools """ +"""XMPP uri parsing tools""" from typing import Optional import sys diff --git a/libervia/backend/tools/config.py b/libervia/backend/tools/config.py index 007932c1..e3504ced 100644 --- a/libervia/backend/tools/config.py +++ b/libervia/backend/tools/config.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" Configuration related useful methods """ +"""Configuration related useful methods""" import os import csv diff --git a/libervia/backend/tools/stream.py b/libervia/backend/tools/stream.py index 87514248..4315b7d5 100644 --- a/libervia/backend/tools/stream.py +++ b/libervia/backend/tools/stream.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" interfaces """ +"""interfaces""" from argparse import OPTIONAL from pathlib import Path diff --git a/libervia/backend/tools/utils.py b/libervia/backend/tools/utils.py index b4c43efb..57a39a89 100644 --- a/libervia/backend/tools/utils.py +++ b/libervia/backend/tools/utils.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -""" various useful methods """ +"""various useful methods""" from typing import Optional, Union import unicodedata diff --git a/libervia/cli/base.py b/libervia/cli/base.py index 3375977a..bf89aef0 100644 --- a/libervia/cli/base.py +++ b/libervia/cli/base.py @@ -54,7 +54,6 @@ log.basicConfig(level=log.WARNING, format="[%(name)s] %(message)s") ### - ## bridge handling # we get bridge name from conf and initialise the right class accordingly main_config = config.parse_main_conf() @@ -91,6 +90,7 @@ PROGRESS_DELAY = 0.1 # the progression will be checked every PROGRESS_DELAY s def date_decoder(arg): return date_utils.date_parse_ext(arg, default_tz=date_utils.TZ_LOCAL) + def optional_bool_decoder(arg: str) -> bool: """Decode an optional string to a boolean value. @@ -99,9 +99,9 @@ def optional_bool_decoder(arg: str) -> bool: @raise ValueError: If the input string is not a valid boolean representation. """ lower_arg = arg.lower() - if lower_arg in ['true', '1', 't', 'y', 'yes']: + if lower_arg in ["true", "1", "t", "y", "yes"]: return True - elif lower_arg in ['false', '0', 'f', 'n', 'no']: + elif lower_arg in ["false", "0", "f", "n", "no"]: return False else: raise ValueError(f"Invalid boolean value: {arg}") diff --git a/libervia/cli/cmd_bookmarks.py b/libervia/cli/cmd_bookmarks.py index 8e9c80b9..c44091bb 100644 --- a/libervia/cli/cmd_bookmarks.py +++ b/libervia/cli/cmd_bookmarks.py @@ -33,9 +33,11 @@ class BookmarksList(base.CommandBase): def __init__(self, host): extra_outputs = {"default": self.default_output} super().__init__( - host, "list", help=_("list bookmarks"), + host, + "list", + help=_("list bookmarks"), use_output=C.OUTPUT_COMPLEX, - extra_outputs=extra_outputs + extra_outputs=extra_outputs, ) def add_parser_options(self): @@ -55,16 +57,16 @@ class BookmarksList(base.CommandBase): conference_data.get("name", ""), conference_data.get("nick", ""), conference_data.get("password", ""), - "✅" if conference_data.get("autojoin", False) else "❌" + "✅" if conference_data.get("autojoin", False) else "❌", ) self.console.print(table) async def start(self): try: - data = data_format.deserialise(await self.host.bridge.bookmarks_list( - "", self.host.profile - )) + data = data_format.deserialise( + await self.host.bridge.bookmarks_list("", self.host.profile) + ) except Exception as e: self.disp(f"can't get bookmarks list: {e}", error=True) self.host.quit(C.EXIT_BRIDGE_ERRBACK) @@ -79,9 +81,7 @@ class BookmarksRemove(base.CommandBase): super().__init__(host, "remove", help=_("remove a bookmark")) def add_parser_options(self): - self.parser.add_argument( - "bookmark", help=_("jid of the bookmark to remove") - ) + self.parser.add_argument("bookmark", help=_("jid of the bookmark to remove")) self.parser.add_argument( "-f", "--force", @@ -92,14 +92,13 @@ class BookmarksRemove(base.CommandBase): async def start(self): if not self.args.force: await self.host.confirm_or_quit( - _("Are you sure to delete the bookmark {bookmark_id!r}?") - .format(bookmark_id=self.args.bookmark) + _("Are you sure to delete the bookmark {bookmark_id!r}?").format( + bookmark_id=self.args.bookmark + ) ) try: - await self.host.bridge.bookmark_remove( - self.args.bookmark, self.host.profile - ) + await self.host.bridge.bookmark_remove(self.args.bookmark, self.host.profile) except Exception as e: self.disp(_("can't delete bookmark: {e}").format(e=e), error=True) self.host.quit(C.EXIT_BRIDGE_ERRBACK) @@ -110,9 +109,7 @@ class BookmarksRemove(base.CommandBase): class BookmarksSet(base.CommandBase): def __init__(self, host): - super().__init__( - host, "set", help=_("add or update a bookmark") - ) + super().__init__(host, "set", help=_("add or update a bookmark")) def add_parser_options(self): self.parser.add_argument("bookmark", help=_("jid of the chat room")) @@ -133,18 +130,16 @@ class BookmarksSet(base.CommandBase): help=_("join the conference room"), ) self.parser.add_argument( - "-N", - "--nick", help=_("preferred roomnick for the chatroom") + "-N", "--nick", help=_("preferred roomnick for the chatroom") ) self.parser.add_argument( - "-P", - "--password", help=_("password used to access the chatroom") + "-P", "--password", help=_("password used to access the chatroom") ) self.parser.add_argument( "-u", "--update", action="store_true", - help=_("update bookmark data instead of replacing") + help=_("update bookmark data instead of replacing"), ) async def start(self): @@ -167,7 +162,7 @@ class BookmarksSet(base.CommandBase): self.disp( f"Can't find existing bookmark {self.args.bookmark!r}: {e}. We " "create it.", - error=True + error=True, ) else: old_conference_data.update(conference_data) @@ -190,6 +185,4 @@ class Bookmarks(base.CommandBase): subcommands = (BookmarksList, BookmarksSet, BookmarksRemove, BookmarksLegacy) def __init__(self, host): - super().__init__( - host, "bookmarks", use_profile=False, help=_("manage bookmarks") - ) + super().__init__(host, "bookmarks", use_profile=False, help=_("manage bookmarks")) diff --git a/libervia/cli/cmd_pubsub.py b/libervia/cli/cmd_pubsub.py index 2cfa0210..4331eb9f 100644 --- a/libervia/cli/cmd_pubsub.py +++ b/libervia/cli/cmd_pubsub.py @@ -1592,9 +1592,7 @@ class List(base.CommandBase): ) async def start(self): - options = { - "depth": self.args.depth - } + options = {"depth": self.args.depth} try: result_s = await self.host.bridge.ps_disco_get( self.args.service, @@ -1617,7 +1615,6 @@ class List(base.CommandBase): node_tree.add(self.recursive_tree(child)) return node_tree - def default_output(self, data: list) -> None: """Output the node as a tree view.""" for item in data: diff --git a/libervia/frontends/quick_frontend/quick_contact_list.py b/libervia/frontends/quick_frontend/quick_contact_list.py index 5add0031..70c6d483 100644 --- a/libervia/frontends/quick_frontend/quick_contact_list.py +++ b/libervia/frontends/quick_frontend/quick_contact_list.py @@ -17,7 +17,7 @@ # along with this program. If not, see . """Contact List handling multi profiles at once, - should replace quick_contact_list module in the future""" +should replace quick_contact_list module in the future""" from libervia.backend.core.i18n import _ from libervia.backend.core.log import getLogger diff --git a/libervia/frontends/tools/xmltools.py b/libervia/frontends/tools/xmltools.py index ba6779f1..6b3816db 100644 --- a/libervia/frontends/tools/xmltools.py +++ b/libervia/frontends/tools/xmltools.py @@ -17,7 +17,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -"""This library help manage XML used in SàT frontends """ +"""This library help manage XML used in SàT frontends""" # we don't import minidom as a different class can be used in frontends # (e.g. NativeDOM in Libervia) diff --git a/stubs/twisted/words/protocols/jabber/error.pyi b/stubs/twisted/words/protocols/jabber/error.pyi index f9d17790..1dc57ff1 100644 --- a/stubs/twisted/words/protocols/jabber/error.pyi +++ b/stubs/twisted/words/protocols/jabber/error.pyi @@ -2,7 +2,6 @@ from typing import Optional from twisted.words.xish import domish - class BaseError(Exception): namespace: str condition: str @@ -10,5 +9,4 @@ class BaseError(Exception): textLang: Optional[str] appCondition: Optional[domish.Element] -class StanzaError(BaseError): - ... +class StanzaError(BaseError): ... diff --git a/stubs/twisted/words/protocols/jabber/jid.pyi b/stubs/twisted/words/protocols/jabber/jid.pyi index 7363a5ad..51ad9af8 100644 --- a/stubs/twisted/words/protocols/jabber/jid.pyi +++ b/stubs/twisted/words/protocols/jabber/jid.pyi @@ -1,11 +1,9 @@ from typing import Tuple, overload - User = str Host = str Resource = str - # Note: these typings are incomplete and evolve as needed. class JID: user: User @@ -13,18 +11,9 @@ class JID: resource: Resource @overload - def __init__(self, str: str, tuple: None = None) -> None: - ... - + def __init__(self, str: str, tuple: None = None) -> None: ... @overload - def __init__(self, str: None, tuple: Tuple[User, Host, Resource]) -> None: - ... - - def userhost(self) -> str: - ... - - def userhostJID(self) -> "JID": - ... - - def full(self) -> str: - ... + def __init__(self, str: None, tuple: Tuple[User, Host, Resource]) -> None: ... + def userhost(self) -> str: ... + def userhostJID(self) -> "JID": ... + def full(self) -> str: ... diff --git a/stubs/twisted/words/xish/domish.pyi b/stubs/twisted/words/xish/domish.pyi index a55538ee..38dd7636 100644 --- a/stubs/twisted/words/xish/domish.pyi +++ b/stubs/twisted/words/xish/domish.pyi @@ -1,8 +1,15 @@ from typing import ( - Dict, Iterator, List, Literal, Optional, Tuple, TypeVar, Union, overload + Dict, + Iterator, + List, + Literal, + Optional, + Tuple, + TypeVar, + Union, + overload, ) - URI = str Name = str QName = Tuple[Optional[URI], Name] @@ -10,10 +17,8 @@ AttributeKey = Union[QName, Name] Attributes = Dict[AttributeKey, str] Prefix = str - D = TypeVar("D") - # Note: these typings are incomplete and evolve as needed. class Element: uri: Optional[URI] @@ -27,54 +32,31 @@ class Element: qname: QName, defaultUri: Optional[URI] = None, attribs: Optional[Attributes] = None, - localPrefixes: Optional[Dict[URI, Prefix]] = None - ) -> None: - ... - - def __getitem__(self, key: AttributeKey) -> str: - ... - - def __setitem__(self, key: AttributeKey, value: str) -> None: - ... - + localPrefixes: Optional[Dict[URI, Prefix]] = None, + ) -> None: ... + def __getitem__(self, key: AttributeKey) -> str: ... + def __setitem__(self, key: AttributeKey, value: str) -> None: ... @overload def getAttribute( - self, - attribname: AttributeKey, - default: None = None - ) -> Union[str, None]: - ... - + self, attribname: AttributeKey, default: None = None + ) -> Union[str, None]: ... @overload - def getAttribute(self, attribname: AttributeKey, default: D) -> Union[str, D]: - ... - - def addChild(self, node: "Element") -> "Element": - ... - - def addContent(self, text: str) -> str: - ... - + def getAttribute(self, attribname: AttributeKey, default: D) -> Union[str, D]: ... + def addChild(self, node: "Element") -> "Element": ... + def addContent(self, text: str) -> str: ... def addElement( self, name: Union[QName, Name], defaultUri: Optional[URI] = None, - content: Optional[str] = None - ) -> "Element": - ... - + content: Optional[str] = None, + ) -> "Element": ... def elements( - self, - uri: Optional[URI] = None, - name: Optional[Name] = None - ) -> Iterator["Element"]: - ... - + self, uri: Optional[URI] = None, name: Optional[Name] = None + ) -> Iterator["Element"]: ... def toXml( self, prefixes: Optional[Dict[URI, Prefix]] = None, closeElement: Literal[0, 1] = 1, defaultUri: str = "", - prefixesInScope: Optional[List[Prefix]] = None - ) -> str: - ... + prefixesInScope: Optional[List[Prefix]] = None, + ) -> str: ... diff --git a/tests/unit/test_email_gateway.py b/tests/unit/test_email_gateway.py index 7f045811..747dfa2b 100644 --- a/tests/unit/test_email_gateway.py +++ b/tests/unit/test_email_gateway.py @@ -42,6 +42,7 @@ from libervia.backend.plugins.plugin_comp_email_gateway.pubsub_service import ( ) from libervia.backend.plugins.plugin_xep_0131 import HeadersData, Urgency + @pytest.fixture def email_gw(host): email_gw = EmailGatewayComponent(host) @@ -49,6 +50,7 @@ def email_gw(host): email_gw.users_data = {} return email_gw + class TestEmailGatewayComponent: def test_jid_to_email_gateway_jid(self, email_gw): @@ -284,12 +286,26 @@ class TestPubsubService: request.sender = client.jid request.nodeIdentifier = pubsub_resource._pfs.namespace files = [ - {"id": "1", "name": "file1", "media_type": "application", "media_subtype": - "octet-stream", "size": 123, "hash_algo": "sha-256", "file_hash": "0123456789abcdef", - "created": 123}, - {"id": "2", "name": "file2", "media_type": "application", "media_subtype": - "octet-stream", "size": 456, "hash_algo": "sha-256", "file_hash": "0123456789abcdef", - "created": 123}, + { + "id": "1", + "name": "file1", + "media_type": "application", + "media_subtype": "octet-stream", + "size": 123, + "hash_algo": "sha-256", + "file_hash": "0123456789abcdef", + "created": 123, + }, + { + "id": "2", + "name": "file2", + "media_type": "application", + "media_subtype": "octet-stream", + "size": 456, + "hash_algo": "sha-256", + "file_hash": "0123456789abcdef", + "created": 123, + }, ] host.memory.get_files = AsyncMock(return_value=files) result, _ = await pubsub_resource.items(request) diff --git a/tests/unit/test_memory_sqla.py b/tests/unit/test_memory_sqla.py index 4f48fee3..b6c0eb5b 100644 --- a/tests/unit/test_memory_sqla.py +++ b/tests/unit/test_memory_sqla.py @@ -51,14 +51,13 @@ class TestSQLA: from_jid=None, to_jid=None, filters={"keyword": "test"}, - profile="test_profile" + profile="test_profile", ) # We check that the SQL query is as expected. executed_stmt = mock_session_instance.execute.call_args[0][0] compiled = executed_stmt.compile( - dialect=sqlite.dialect(), - compile_kwargs={"literal_binds": True} + dialect=sqlite.dialect(), compile_kwargs={"literal_binds": True} ) sql = str(compiled) assert "json_each" in sql diff --git a/tests/unit/test_plugin_xep_0215.py b/tests/unit/test_plugin_xep_0215.py index 799a880e..5b9ff1b9 100644 --- a/tests/unit/test_plugin_xep_0215.py +++ b/tests/unit/test_plugin_xep_0215.py @@ -31,7 +31,6 @@ class TestXEP0215: host.memory.disco.has_feature = AsyncMock() host.memory.disco.has_feature.return_value = True - services_elt = xml_tools.parse( """ diff --git a/tests/unit/test_plugin_xep_0461.py b/tests/unit/test_plugin_xep_0461.py index 69b44df4..c4701912 100644 --- a/tests/unit/test_plugin_xep_0461.py +++ b/tests/unit/test_plugin_xep_0461.py @@ -35,7 +35,9 @@ class TestXEP0461: async def test_reply_to_from_element(self): """ element parsing works.""" # Test correct case - element = domish.Element((NS_REPLY, "reply"), attribs={"id": "123", "to": "user@example.com"}) + element = domish.Element( + (NS_REPLY, "reply"), attribs={"id": "123", "to": "user@example.com"} + ) reply = ReplyTo.from_element(element) assert reply.to == jid.JID("user@example.com") assert reply.id == "123" @@ -92,5 +94,7 @@ class TestXEP0461: mess_data = MessageData(extra={"reply": {"id": "123", "internal_uid": False}}) pre_xml_treatments = MagicMock() post_xml_treatments = MagicMock() - xep._sendMessage_trigger(client, mess_data, pre_xml_treatments, post_xml_treatments) + xep._sendMessage_trigger( + client, mess_data, pre_xml_treatments, post_xml_treatments + ) assert post_xml_treatments.addCallback.called diff --git a/twisted/plugins/libervia_backend_plugin.py b/twisted/plugins/libervia_backend_plugin.py index c28d7517..571086a1 100644 --- a/twisted/plugins/libervia_backend_plugin.py +++ b/twisted/plugins/libervia_backend_plugin.py @@ -36,6 +36,7 @@ def initialise(options): """Method to initialise global modules""" # XXX: We need to configure logs before any log method is used, so here is the best place. from libervia.backend.core import log_config + log_config.libervia_configure(C.LOG_BACKEND_TWISTED, C, backend_data=options) @@ -52,11 +53,13 @@ class LiberviaMaker: def set_debugger(self): from twisted.internet import defer + if defer.Deferred.debug: # if we are in debug mode, we want to use ipdb instead of pdb try: import ipdb import pdb + pdb.set_trace = ipdb.set_trace pdb.post_mortem = ipdb.post_mortem except ImportError: @@ -64,12 +67,14 @@ class LiberviaMaker: def makeService(self, options): from twisted.internet import asyncioreactor + asyncioreactor.install() self.set_debugger() # XXX: Libervia must be imported after log configuration, # because it write stuff to logs initialise(options.parent) from libervia.backend.core.main import LiberviaBackend + return LiberviaBackend()