Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-rt for openSUSE:Factory 
checked in at 2026-03-25 21:19:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-rt (Old)
 and      /work/SRC/openSUSE:Factory/.python-rt.new.8177 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-rt"

Wed Mar 25 21:19:57 2026 rev:26 rq:1342395 version:3.6.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-rt/python-rt.changes      2026-01-08 
15:29:53.651239189 +0100
+++ /work/SRC/openSUSE:Factory/.python-rt.new.8177/python-rt.changes    
2026-03-27 06:49:01.420776599 +0100
@@ -1,0 +2,12 @@
+Wed Mar 25 08:07:01 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 3.6.0:
+  * Parameters search_params and catalog_id used in asset search
+    are now optional
+  * If catalog_id is set to None, search all catalogs
+  * Added option to define all query parameters for get/search
+    asset and ticket endpoints
+  * Fixed typing of methods that returned a partially unknown
+    dictionary
+
+-------------------------------------------------------------------

Old:
----
  rt-3.4.0.tar.gz

New:
----
  rt-3.6.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-rt.spec ++++++
--- /var/tmp/diff_new_pack.4PetH6/_old  2026-03-27 06:49:02.760831782 +0100
+++ /var/tmp/diff_new_pack.4PetH6/_new  2026-03-27 06:49:02.760831782 +0100
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-rt
-Version:        3.4.0
+Version:        3.6.0
 Release:        0
 Summary:        Python interface to Request Tracker API
 License:        GPL-3.0-only

++++++ rt-3.4.0.tar.gz -> rt-3.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/.codespell_ignore 
new/python-rt-3.6.0/.codespell_ignore
--- old/python-rt-3.4.0/.codespell_ignore       2025-11-28 08:11:12.000000000 
+0100
+++ new/python-rt-3.6.0/.codespell_ignore       2026-02-06 23:13:59.000000000 
+0100
@@ -1,2 +1,2 @@
 requestor
-requestors
\ No newline at end of file
+requestors
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/.github/ISSUE_TEMPLATE/bug_report.md 
new/python-rt-3.6.0/.github/ISSUE_TEMPLATE/bug_report.md
--- old/python-rt-3.4.0/.github/ISSUE_TEMPLATE/bug_report.md    2025-11-28 
08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/.github/ISSUE_TEMPLATE/bug_report.md    2026-02-06 
23:13:59.000000000 +0100
@@ -21,10 +21,10 @@
 A clear and concise description of what you expected to happen.
 
 **Environment (please complete the following information):**
- - OS: 
- - RT version: 
- - Python version: 
- - python-rt version: 
+ - OS:
+ - RT version:
+ - Python version:
+ - python-rt version:
 
 **Additional context**
 Add any other context about the problem here.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/.pre-commit-config.yaml 
new/python-rt-3.6.0/.pre-commit-config.yaml
--- old/python-rt-3.4.0/.pre-commit-config.yaml 1970-01-01 01:00:00.000000000 
+0100
+++ new/python-rt-3.6.0/.pre-commit-config.yaml 2026-02-06 23:13:59.000000000 
+0100
@@ -0,0 +1,56 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+default_language_version:
+  python: ">=3.13"
+
+exclude: |
+  (?x)^(
+    doc/.*|
+    rt.egg-info/.*|
+    venv/.*|
+    .venv/.*|
+    noxfile.py|
+  )$
+
+repos:
+  - repo: builtin
+    hooks:
+      - id: check-added-large-files
+        args: ['--maxkb=750']
+        exclude: ^uv.lock$
+      - id: end-of-file-fixer
+      - id: trailing-whitespace
+        args: [--markdown-linebreak-ext=md]
+      - id: fix-byte-order-marker
+      - id: check-json
+      - id: check-toml
+      - id: check-yaml
+      - id: mixed-line-ending
+        args: [--fix=lf]
+      - id: check-symlinks
+      - id: check-merge-conflict
+      - id: check-executables-have-shebangs
+
+  - repo: local
+    hooks:
+      - id: local-ruff-check
+        name: ruff check
+        entry: uv run ruff check --force-exclude --fix --exit-non-zero-on-fix
+        require_serial: true
+        language: system
+        types: [python]
+
+      - id: local-ruff-format
+        name: ruff format
+        entry: uv run ruff format --force-exclude --exit-non-zero-on-format
+        require_serial: true
+        language: system
+        types: [python]
+
+      - id: mypy
+        name: mypy
+        entry: uv run mypy
+        files: rt
+        require_serial: true
+        language: python
+        types: [python]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/CHANGELOG.md 
new/python-rt-3.6.0/CHANGELOG.md
--- old/python-rt-3.4.0/CHANGELOG.md    2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/CHANGELOG.md    2026-02-06 23:13:59.000000000 +0100
@@ -3,6 +3,16 @@
 
 This project adheres to [Semantic 
Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [v3.6.0], 2026-02-06
+### Added
+- Parameters search_params and catalog_id used in asset search are now optional
+- If catalog_id is set to None, search all catalogs
+
+## [v3.5.0], 2026-02-04
+### Added
+- Added option to define all query parameters for get/search asset and ticket 
endpoints
+- Fixed typing of methods that returned a partially unknown dictionary
+
 ## [v3.4.0], 2025-11-21
 ### Added
 - Added functionality for some of the asset endpoints (get, create, edit, 
search, get history)
@@ -141,9 +151,9 @@
 - Importing the `rt` class changed in order to better accommodate the new 
`rest2` implementation.
   - Where one use to be able to import `rt` using:
     `from rt import Rt`
-  
+
     you now have to use the following syntax:
-  
+
     `from rt.rest1 import Rt`
 - Importing the `rt` module does no longer import all exceptions but only the 
core `RtError` exception.
 If you require other exceptions, please import them from `rt.exceptions`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/README.rst 
new/python-rt-3.6.0/README.rst
--- old/python-rt-3.4.0/README.rst      2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/README.rst      2026-02-06 23:13:59.000000000 +0100
@@ -8,7 +8,7 @@
     :target: https://badge.fury.io/py/rt
 
 ==============================================
- Rt - Python interface to Request Tracker API 
+ Rt - Python interface to Request Tracker API
 ==============================================
 
 Python implementation of REST API described here:
@@ -108,7 +108,7 @@
     >>>                 print(content)
 
 
-               
+
 Please use docstrings to see how to use different functions. They are written
 in ReStructuredText. You can also generate HTML documentation by running
 ``make html`` in doc directory (Sphinx required).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/doc/conf.py 
new/python-rt-3.6.0/doc/conf.py
--- old/python-rt-3.4.0/doc/conf.py     2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/doc/conf.py     2026-02-06 23:13:59.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
 #
 # rt documentation build configuration file, created by
 # sphinx-quickstart on Thu Jan 10 16:31:25 2013.
@@ -11,9 +10,9 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
+import importlib.metadata
 import os
 import sys
-import importlib.metadata
 
 sys.path.insert(0, os.path.abspath('..'))
 
@@ -35,7 +34,7 @@
     'member-order': 'alphabetical',
     'special-members': '__init__',
     'undoc-members': True,
-    'exclude-members': '__weakref__'
+    'exclude-members': '__weakref__',
 }
 
 # Add any paths that contain templates here, relative to this directory.
@@ -51,8 +50,8 @@
 master_doc = 'index'
 
 # General information about the project.
-project = u'rt'
-copyright = u'2013-present, A project founded by Jiri Machalek and maintained 
by the python-rt community.'
+project = 'rt'
+copyright = '2013-present, A project founded by Jiri Machalek and maintained 
by the python-rt community.'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -186,10 +185,8 @@
 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': '',
 }
@@ -197,8 +194,7 @@
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass 
[howto/manual]).
 latex_documents = [
-    ('index', 'rt.tex', u'rt Documentation',
-     u'Jiri Machalek', 'manual'),
+    ('index', 'rt.tex', 'rt Documentation', 'Jiri Machalek', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -226,10 +222,7 @@
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'rt', u'rt Documentation',
-     [u'Jiri Machalek'], 1)
-]
+man_pages = [('index', 'rt', 'rt Documentation', ['Jiri Machalek'], 1)]
 
 # If true, show URL addresses after external links.
 # man_show_urls = False
@@ -241,9 +234,7 @@
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-    ('index', 'rt', u'rt Documentation',
-     u'Jiri Machalek', 'rt', 'One line description of project.',
-     'Miscellaneous'),
+    ('index', 'rt', 'rt Documentation', 'Jiri Machalek', 'rt', 'One line 
description of project.', 'Miscellaneous'),
 ]
 
 # Documents to append as an appendix to all manuals.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/doc/glossary.rst 
new/python-rt-3.6.0/doc/glossary.rst
--- old/python-rt-3.4.0/doc/glossary.rst        2025-11-28 08:11:12.000000000 
+0100
+++ new/python-rt-3.6.0/doc/glossary.rst        2026-02-06 23:13:59.000000000 
+0100
@@ -12,4 +12,3 @@
     REST
         Representational state transfer,  a style of software architecture for
         distributed hypermedia systems such as the World Wide Web
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/doc/index.rst 
new/python-rt-3.6.0/doc/index.rst
--- old/python-rt-3.4.0/doc/index.rst   2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/doc/index.rst   2026-02-06 23:13:59.000000000 +0100
@@ -42,7 +42,7 @@
     pip install rt
 
 Using project git repository::
-    
+
     git clone https://github.com/python-rt/python-rt
 
 Indices and tables
@@ -51,4 +51,3 @@
 * :ref:`genindex`
 * :ref:`modindex`
 * :ref:`search`
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/noxfile.py 
new/python-rt-3.6.0/noxfile.py
--- old/python-rt-3.4.0/noxfile.py      2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/noxfile.py      2026-02-06 23:13:59.000000000 +0100
@@ -12,5 +12,5 @@
 
 @nox.session()
 def lint(session):
-    session.install(".[test,dev]")
+    session.install('.[test,dev]')
     session.run('ruff', 'rt')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/pyproject.toml 
new/python-rt-3.6.0/pyproject.toml
--- old/python-rt-3.4.0/pyproject.toml  2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/pyproject.toml  2026-02-06 23:13:59.000000000 +0100
@@ -47,6 +47,7 @@
   "types-requests",
   "codespell",
   "bandit",
+  "prek>=0.3.2",
 ]
 test = [
   "pytest~=8.3",
@@ -97,7 +98,11 @@
 line-length = 140
 indent-width = 4
 target-version = "py39"
-include = ["pyproject.toml", "rt/**/*.py", "tests/**/*.py"]
+include = [
+  "pyproject.toml",
+  "rt/**/*.py",
+  "tests/**/*.py"
+]
 
 [tool.ruff.lint]
 select = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/rt/rest1.py 
new/python-rt-3.6.0/rt/rest1.py
--- old/python-rt-3.4.0/rt/rest1.py     2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/rt/rest1.py     2026-02-06 23:13:59.000000000 +0100
@@ -258,7 +258,7 @@
         :rtype: int
         """
         try:
-            return int(msg.split('\n')[0].split(' ')[1])
+            return int(msg.split('\n', maxsplit=1)[0].split(' ')[1])
         except (ValueError, AttributeError, LookupError):
             return None
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/rt/rest2.py 
new/python-rt-3.6.0/rt/rest2.py
--- old/python-rt-3.4.0/rt/rest2.py     2025-11-28 08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/rt/rest2.py     2026-02-06 23:13:59.000000000 +0100
@@ -409,7 +409,7 @@
 
         return res
 
-    def new_correspondence(self, queue: typing.Optional[typing.Union[str, 
object]] = None) -> typing.Iterator[dict]:
+    def new_correspondence(self, queue: typing.Optional[typing.Union[str, 
object]] = None) -> typing.Iterator[dict[str, typing.Any]]:
         """Obtains tickets changed by other users than the system one.
 
         :param queue: Queue where to search
@@ -421,7 +421,7 @@
         """
         return self.search(queue=queue, order='-LastUpdated')
 
-    def last_updated(self, since: str, queue: typing.Optional[str] = None) -> 
typing.Iterator[dict]:
+    def last_updated(self, since: str, queue: typing.Optional[str] = None) -> 
typing.Iterator[dict[str, typing.Any]]:
         """Obtains tickets changed after given date.
 
         :param since: Date as string in form '2011-02-24'
@@ -460,9 +460,9 @@
         queue: typing.Optional[typing.Union[str, object]] = None,
         order: typing.Optional[str] = None,
         raw_query: typing.Optional[str] = None,
-        query_format: typing.Union[str, list[str]] = 'l',
+        query_format: typing.Union[str, list[str], dict[str, str]] = 'l',
         **kwargs: typing.Any,
-    ) -> typing.Iterator[dict]:
+    ) -> typing.Iterator[dict[str, typing.Any]]:
         r"""Search arbitrary needles in given fields and queue.
 
         Example::
@@ -554,6 +554,8 @@
 
         if isinstance(query_format, list):
             get_params['fields'] = ','.join(query_format)
+        elif isinstance(query_format, dict):
+            get_params = {**get_params, **query_format}
         elif query_format == 'l':
             get_params['fields'] = (
                 
'Owner,Status,Created,Subject,Queue,CustomFields,Requestor,Cc,AdminCc,Started,Created,TimeEstimated,Due,Type,InitialPriority,Priority,TimeLeft,LastUpdated'
@@ -564,13 +566,16 @@
 
         yield from self.__paged_request(url, params=get_params)
 
-    def get_ticket(self, ticket_id: typing.Union[str, int]) -> dict:
+    def get_ticket(
+        self, ticket_id: typing.Union[str, int], query_format: 
typing.Union[dict[str, str], None] = None
+    ) -> dict[str, typing.Any]:
         """Fetch ticket by its ID.
 
         :param ticket_id: ID of demanded ticket
+        :param query_format: Returned fields to be populated
 
         :returns: Dictionary with key, value pairs for ticket with
-                  *ticket_id* or None if ticket does not exist. List of keys:
+                  *ticket_id* or None if ticket does not exist. List of keys 
(when query_format is None):
 
                       * id
                       * numerical_id
@@ -597,7 +602,10 @@
         :raises UnexpectedMessageFormatError: Unexpected format of returned 
message.
         :raises NotFoundError: If there is no ticket with the specified 
ticket_id.
         """
-        res = self.__request(f'ticket/{ticket_id}', 
get_params={'fields[Queue]': 'Name'})
+        if not query_format:
+            query_format = {}
+
+        res = self.__request(f'ticket/{ticket_id}', 
get_params={'fields[Queue]': 'Name', **query_format})
 
         if not isinstance(res, dict):  # pragma: no cover
             raise UnexpectedResponseError(str(res))
@@ -916,7 +924,7 @@
 
         return attachments
 
-    def get_attachment(self, attachment_id: typing.Union[str, int]) -> dict:
+    def get_attachment(self, attachment_id: typing.Union[str, int]) -> 
dict[str, typing.Any]:
         """Get attachment.
 
         :param attachment_id: ID of attachment to fetch
@@ -1628,12 +1636,13 @@
 
         return response
 
-    def get_asset(self, asset_id: typing.Union[str, int]) -> dict[str, 
typing.Any]:
+    def get_asset(self, asset_id: typing.Union[str, int], query_format: 
typing.Optional[dict[str, str]] = None) -> dict[str, typing.Any]:
         """
         Get asset.
 
         :param asset_id: Asset ID.
-        :return: Asset.
+        :param query_format: Returned fields to be populated.
+        :return: Asset (when `query_format` is `None`).
                 id: int
                 Lifecycle: str
                 Disabled: str
@@ -1651,7 +1660,10 @@
                 Owner: dict[str, str]
                 CustomFields: list[dict[str, typing.Any]]
         """
-        response = self.__request(f'asset/{asset_id}')
+        if not query_format:
+            query_format = {}
+
+        response = self.__request(f'asset/{asset_id}', get_params=query_format)
 
         self.logger.debug(str(response))
 
@@ -1696,23 +1708,28 @@
         return isinstance(response, list)
 
     def search_assets(
-        self, catalog_id: typing.Union[str, int], search_params: 
list[dict[str, typing.Any]], fields: str = "Owner,Description,Status"
+        self,
+        catalog_id: typing.Union[str, int, None] = None,
+        search_params: typing.Union[list[dict[str, typing.Any]], None] = None,
+        query_format: typing.Optional[typing.Union[str, list[str], dict[str, 
str]]] = None,
     ) -> typing.Iterator[dict[str, typing.Any]]:
         """
         Search assets in a catalog.
 
         Example::
 
-            client = Rt(...)
-            client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}])
+            >>> client = Rt(...)
+            >>> client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}], "Owner,Status")
+            >>> client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}], ["Owner", "Status"])
+            >>> client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}], {"fields": "Owner,Status", "fields[Owner]": "id,Name"})
 
-        :param catalog_id: Catalog ID.
+        :param catalog_id: Catalog ID. Use `None` to search all catalogs.
         :param search_params: Params used to filter the results.
             field: str
             value: str | int
             operator: Literal[">", "<", "=", "!=", "LIKE", "NOT LIKE", ">=", 
"<="] | None
-        :param fields: Fields to return separated by a comma.
-        :return: Found assets. The following is returned with the default 
`fields`
+        :param query_format: Returned fields to be populated.
+        :return: Found assets. The following is returned by default (when 
`query_format` is `None`)
             {
                 'Description': '',
                 'id': '1',
@@ -1722,9 +1739,22 @@
                 'type': 'asset'
             }
         """
-        search_params.append({'field': 'Catalog', 'value': catalog_id, 
'operator': '='})
+        if not search_params:
+            search_params = []
+        if catalog_id:
+            search_params.append({'field': 'Catalog', 'value': catalog_id, 
'operator': '='})
+        else:
+            search_params.append({'field': 'Catalog', 'value': '%', 
'operator': 'LIKE'})
+
+        get_params = {'fields': 'Owner,Description,Status'}
+        if isinstance(query_format, dict):
+            get_params = {**get_params, **query_format}
+        elif isinstance(query_format, list):
+            get_params['fields'] = ','.join(query_format)
+        elif isinstance(query_format, str):
+            get_params['fields'] = query_format
 
-        yield from self.__paged_request('assets', json_data=search_params, 
params={"fields": fields})
+        yield from self.__paged_request('assets', json_data=search_params, 
params=get_params)
 
     def get_asset_history(self, asset_id: typing.Union[str, int]) -> 
typing.Iterator[dict[str, typing.Any]]:
         """
@@ -1961,7 +1991,7 @@
         page: int = 1,
         per_page: int = 20,
         recurse: bool = True,
-    ) -> collections.abc.AsyncIterator:
+    ) -> collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """Request using pagination for :term:`API`.
 
         :param selector: End part of URL which completes self.url parameter
@@ -2080,7 +2110,9 @@
 
         return res
 
-    async def new_correspondence(self, queue: 
typing.Optional[typing.Union[str, object]] = None) -> 
collections.abc.AsyncIterator:
+    async def new_correspondence(
+        self, queue: typing.Optional[typing.Union[str, object]] = None
+    ) -> collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """Obtains tickets changed by other users than the system one.
 
         :param queue: Queue where to search
@@ -2089,11 +2121,11 @@
                   the system one, ordered in decreasing order by LastUpdated.
                   Each ticket is dictionary, the same as in
                   :py:meth:`~Rt.get_ticket`.
-                  collections.abc.AsyncIterator[dict]
+                  collections.abc.AsyncIterator[dict[str, typing.Any]]
         """
         return self.search(queue=queue, order='-LastUpdated')
 
-    async def last_updated(self, since: str, queue: typing.Optional[str] = 
None) -> collections.abc.AsyncIterator:
+    async def last_updated(self, since: str, queue: typing.Optional[str] = 
None) -> collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """Obtains tickets changed after given date.
 
         :param since: Date as string in form '2011-02-24'
@@ -2103,7 +2135,7 @@
                   *since* ordered in decreasing order by LastUpdated.
                   Each ticket is a dictionary, the same as in
                   :py:meth:`~Rt.get_ticket`.
-                  collections.abc.AsyncIterator[dict]
+                  collections.abc.AsyncIterator[dict[str, typing.Any]]
 
         :raises InvalidUseError: If the specified date is of an unsupported 
format.
         """
@@ -2133,9 +2165,9 @@
         queue: typing.Optional[typing.Union[str, object]] = None,
         order: typing.Optional[str] = None,
         raw_query: typing.Optional[str] = None,
-        query_format: typing.Union[str, list[str]] = 'l',
+        query_format: typing.Union[str, list[str], dict[str, str]] = 'l',
         **kwargs: typing.Any,
-    ) -> collections.abc.AsyncIterator:
+    ) -> collections.abc.AsyncIterator[dict[str, typing.Any]]:
         r"""Search arbitrary needles in given fields and queue.
 
         Example::
@@ -2186,7 +2218,7 @@
 
         :returns: Iterator over matching tickets. Each ticket is the same 
dictionary
                   as in :py:meth:`~Rt.get_ticket`.
-                  collections.abc.AsyncIterator[dict]
+                  collections.abc.AsyncIterator[dict[str, typing.Any]]
         :raises:  UnexpectedMessageFormatError: Unexpected format of returned 
message.
                   InvalidQueryError: If raw query is malformed
         """
@@ -2228,6 +2260,8 @@
 
         if isinstance(query_format, list):
             get_params['fields'] = ','.join(query_format)
+        elif isinstance(query_format, dict):
+            get_params = {**get_params, **query_format}
         elif query_format == 'l':
             get_params['fields'] = (
                 
'Owner,Status,Created,Subject,Queue,CustomFields,Requestor,Cc,AdminCc,Started,Created,TimeEstimated,Due,Type,InitialPriority,Priority,TimeLeft,LastUpdated'
@@ -2239,13 +2273,16 @@
         async for item in self.__paged_request(url, params=get_params):
             yield item
 
-    async def get_ticket(self, ticket_id: typing.Union[str, int]) -> dict:
+    async def get_ticket(
+        self, ticket_id: typing.Union[str, int], query_format: 
typing.Union[dict[str, str], None] = None
+    ) -> dict[str, typing.Any]:
         """Fetch ticket by its ID.
 
         :param ticket_id: ID of demanded ticket
+        :param query_format: Returned fields to be populated
 
         :returns: Dictionary with key, value pairs for ticket with
-                  *ticket_id* or None if ticket does not exist. List of keys:
+                  *ticket_id* or None if ticket does not exist. List of keys 
(when query_format is None):
 
                       * id
                       * numerical_id
@@ -2272,7 +2309,10 @@
         :raises UnexpectedMessageFormatError: Unexpected format of returned 
message.
         :raises NotFoundError: If there is no ticket with the specified 
ticket_id.
         """
-        res = await self.__request(f'ticket/{ticket_id}', 
get_params={'fields[Queue]': 'Name'})
+        if not query_format:
+            query_format = {}
+
+        res = await self.__request(f'ticket/{ticket_id}', 
get_params={'fields[Queue]': 'Name', **query_format})
 
         if not isinstance(res, dict):  # pragma: no cover
             raise UnexpectedResponseError(str(res))
@@ -2374,7 +2414,7 @@
 
         return bool(msg[0])
 
-    async def get_ticket_history(self, ticket_id: typing.Union[str, int]) -> 
collections.abc.AsyncIterator:
+    async def get_ticket_history(self, ticket_id: typing.Union[str, int]) -> 
collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """Get set of short history items.
 
         :param ticket_id: ID of ticket
@@ -2527,7 +2567,7 @@
         self,
         ticket_id: typing.Union[str, int],
         query_filter: typing.Optional[list[dict[str, str]]] = None,
-    ) -> collections.abc.AsyncIterator:
+    ) -> collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """Get attachment list for a given ticket.
 
         Example of a return result:
@@ -2563,7 +2603,7 @@
         self,
         ticket_id: typing.Union[str, int],
         query_filter: typing.Optional[list[dict[str, str]]] = None,
-    ) -> collections.abc.AsyncIterator:
+    ) -> collections.abc.AsyncIterator[int]:
         """Get IDs of attachments for given ticket.
 
         :param ticket_id: ID of
@@ -2581,7 +2621,7 @@
         ):
             yield int(item['id'])
 
-    async def get_attachment(self, attachment_id: typing.Union[str, int]) -> 
dict:
+    async def get_attachment(self, attachment_id: typing.Union[str, int]) -> 
dict[str, typing.Any]:
         """Get attachment.
 
         :param attachment_id: ID of attachment to fetch
@@ -2953,7 +2993,7 @@
 
         return res
 
-    async def get_all_queues(self, include_disabled: bool = False) -> 
collections.abc.AsyncIterator:
+    async def get_all_queues(self, include_disabled: bool = False) -> 
collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """Return a list of all queues.
 
         Example of a return result:
@@ -3293,12 +3333,15 @@
 
         return response
 
-    async def get_asset(self, asset_id: typing.Union[str, int]) -> dict[str, 
typing.Any]:
+    async def get_asset(
+        self, asset_id: typing.Union[str, int], query_format: 
typing.Optional[dict[str, str]] = None
+    ) -> dict[str, typing.Any]:
         """
         Get asset.
 
         :param asset_id: Asset ID.
-        :return: Asset.
+        :param query_format: Returned fields to be populated.
+        :return: Asset (when `query_format` is `None`).
                 id: int
                 Lifecycle: str
                 Disabled: str
@@ -3316,7 +3359,10 @@
                 Owner: dict[str, str]
                 CustomFields: list[dict[str, typing.Any]]
         """
-        response = await self.__request(f'asset/{asset_id}')
+        if not query_format:
+            query_format = {}
+
+        response = await self.__request(f'asset/{asset_id}', 
get_params=query_format)
 
         self.logger.debug(str(response))
 
@@ -3361,23 +3407,28 @@
         return isinstance(response, list)
 
     async def search_assets(
-        self, catalog_id: typing.Union[str, int], search_params: 
list[dict[str, typing.Any]], fields: str = "Owner,Description,Status"
+        self,
+        catalog_id: typing.Union[str, int, None] = None,
+        search_params: typing.Union[list[dict[str, typing.Any]], None] = None,
+        query_format: typing.Optional[typing.Union[str, list[str], dict[str, 
str]]] = None,
     ) -> collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """
         Search assets in a catalog.
 
         Example::
 
-            client = AsyncRt(...)
-            await client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}])
+            >>> client = AsyncRt(...)
+            >>> await client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}], "Owner,Status")
+            >>> await client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}], ["Owner", "Status"])
+            >>> await client.search_assets(1, [{"field": "Name", "value": 
"NameOfMyAsset"}], {"fields": "Owner,Status", "fields[Owner]": "id,Name"})
 
-        :param catalog_id: Catalog ID.
+        :param catalog_id: Catalog ID. Use `None` to search all catalogs.
         :param search_params: Params used to filter the results.
             field: str
             value: str | int
             operator: Literal[">", "<", "=", "!=", "LIKE", "NOT LIKE", ">=", 
"<="] | None
-        :param fields: Fields to return separated by a comma.
-        :return: Found assets. The following is returned with the default 
`fields`
+        :param query_format: Returned fields to be populated.
+        :return: Found assets. The following is returned by default (when 
`query_format` is `None`)
             {
                 'Description': '',
                 'id': '1',
@@ -3387,12 +3438,25 @@
                 'type': 'asset'
             }
         """
-        search_params.append({'field': 'Catalog', 'value': catalog_id, 
'operator': '='})
+        if not search_params:
+            search_params = []
+        if catalog_id:
+            search_params.append({'field': 'Catalog', 'value': catalog_id, 
'operator': '='})
+        else:
+            search_params.append({'field': 'Catalog', 'value': '%', 
'operator': 'LIKE'})
+
+        get_params = {'fields': 'Owner,Description,Status'}
+        if isinstance(query_format, dict):
+            get_params = {**get_params, **query_format}
+        elif isinstance(query_format, list):
+            get_params['fields'] = ','.join(query_format)
+        elif isinstance(query_format, str):
+            get_params['fields'] = query_format
 
-        async for item in self.__paged_request('assets', 
json_data=search_params, params={"fields": fields}):
+        async for item in self.__paged_request('assets', 
json_data=search_params, params=get_params):
             yield item
 
-    async def get_asset_history(self, asset_id: typing.Union[str, int]) -> 
collections.abc.AsyncIterator[list[dict[str, typing.Any]]]:
+    async def get_asset_history(self, asset_id: typing.Union[str, int]) -> 
collections.abc.AsyncIterator[dict[str, typing.Any]]:
         """
         Get asset history.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/tests/test_basic.py 
new/python-rt-3.6.0/tests/test_basic.py
--- old/python-rt-3.4.0/tests/test_basic.py     2025-11-28 08:11:12.000000000 
+0100
+++ new/python-rt-3.6.0/tests/test_basic.py     2026-02-06 23:13:59.000000000 
+0100
@@ -81,6 +81,13 @@
     assert search_result[0]['Status'] == 'new'
     assert 'Requestor' not in search_result[0].keys()
 
+    # search with query_format field dict
+    search_result = list(rt_connection.search(Subject=ticket_subject, 
query_format={'fields': 'Owner', 'fields[Owner]': 'Name'}))
+    assert len(search_result) == 1
+    assert 'Subject' not in search_result[0]
+    assert 'Status' not in search_result[0]
+    assert search_result[0]['Owner']['Name'] == 'Nobody'
+
     # raw search
     search_result = 
list(rt_connection.search(raw_query=f'Subject="{ticket_subject}"'))
     assert len(search_result) == 1
@@ -448,7 +455,10 @@
 
 
 def test_assets(rt_connection: rt.rest2.Rt):
-    asset_id = rt_connection.create_asset('test', 1, Creator='root')
+    asset_name = random_string()
+    asset_name_new = random_string()
+
+    asset_id = rt_connection.create_asset(asset_name, 1, Creator='root')
     assert asset_id
 
     asset = rt_connection.get_asset(asset_id)
@@ -457,10 +467,34 @@
     asset_history = rt_connection.get_asset_history(asset_id)
     assert len(list(asset_history)) == 1
 
-    asset_edited = rt_connection.edit_asset(asset_id, Name='test2')
+    asset_edited = rt_connection.edit_asset(asset_id, Name=asset_name_new)
     assert asset_edited
 
-    search = rt_connection.search_assets(1, [{'field': 'Name', 'value': 
'test2'}])
+    search = rt_connection.search_assets(1, [{'field': 'Name', 'value': 
asset_name_new}])
+    items = list(search)
+    assert len(items) == 1
+    assert items[0]['Status'] == 'new'
+
+    search = rt_connection.search_assets(1, [{'field': 'Name', 'value': 
asset_name_new}], query_format='Owner')
+    items = list(search)
+    assert len(items) == 1
+    assert items[0]['Owner']['id'] == 'Nobody'
+    assert 'Status' not in items[0]
+
+    search = rt_connection.search_assets(1, [{'field': 'Name', 'value': 
asset_name_new}], query_format=['Owner'])
+    items = list(search)
+    assert len(items) == 1
+    assert items[0]['Owner']['id'] == 'Nobody'
+    assert 'Status' not in items[0]
+
+    search = rt_connection.search_assets()
+    items = list(search)
+    assert items
+
+    search = rt_connection.search_assets(
+        1, [{'field': 'Name', 'value': asset_name_new}], 
query_format={'fields': 'Owner', 'fields[Owner]': 'Name'}
+    )
     items = list(search)
     assert len(items) == 1
-    assert items[0]["Status"] == "new"
+    assert items[0]['Owner']['Name'] == 'Nobody'
+    assert 'Status' not in items[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python-rt-3.4.0/tests/test_basic_async.py 
new/python-rt-3.6.0/tests/test_basic_async.py
--- old/python-rt-3.4.0/tests/test_basic_async.py       2025-11-28 
08:11:12.000000000 +0100
+++ new/python-rt-3.6.0/tests/test_basic_async.py       2026-02-06 
23:13:59.000000000 +0100
@@ -84,6 +84,15 @@
     assert search_result[0]['Status'] == 'new'
     assert 'Requestor' not in search_result[0].keys()
 
+    # search with query_format field dict
+    search_result = [
+        item async for item in 
async_rt_connection.search(Subject=ticket_subject, query_format={'fields': 
'Owner', 'fields[Owner]': 'Name'})
+    ]
+    assert len(search_result) == 1
+    assert 'Subject' not in search_result[0]
+    assert 'Status' not in search_result[0]
+    assert search_result[0]['Owner']['Name'] == 'Nobody'
+
     # raw search
     search_result = [item async for item in 
async_rt_connection.search(raw_query=f'Subject="{ticket_subject}"')]
     assert len(search_result) == 1
@@ -461,7 +470,10 @@
 
 @pytest.mark.asyncio
 async def test_assets(async_rt_connection: rt.rest2.AsyncRt):
-    asset_id = await async_rt_connection.create_asset('test', 1, 
Creator='root')
+    asset_name = random_string()
+    asset_name_new = random_string()
+
+    asset_id = await async_rt_connection.create_asset(asset_name, 1, 
Creator='root')
     assert asset_id
 
     asset = await async_rt_connection.get_asset(asset_id)
@@ -470,10 +482,34 @@
     asset_history = [item async for item in 
async_rt_connection.get_asset_history(asset_id)]
     assert len(asset_history) == 1
 
-    asset_edited = await async_rt_connection.edit_asset(asset_id, 
Name='test2async')
+    asset_edited = await async_rt_connection.edit_asset(asset_id, 
Name=asset_name_new)
     assert asset_edited
 
-    search = async_rt_connection.search_assets(1, [{'field': 'Name', 'value': 
'test2async'}])
+    search = async_rt_connection.search_assets(1, [{'field': 'Name', 'value': 
asset_name_new}])
+    items = [item async for item in search]
+    assert len(items) == 1
+    assert items[0]['Status'] == 'new'
+
+    search = async_rt_connection.search_assets(1, [{'field': 'Name', 'value': 
asset_name_new}], query_format='Owner')
+    items = [item async for item in search]
+    assert len(items) == 1
+    assert items[0]['Owner']['id'] == 'Nobody'
+    assert 'Status' not in items[0]
+
+    search = async_rt_connection.search_assets(1, [{'field': 'Name', 'value': 
asset_name_new}], query_format=['Owner'])
+    items = [item async for item in search]
+    assert len(items) == 1
+    assert items[0]['Owner']['id'] == 'Nobody'
+    assert 'Status' not in items[0]
+
+    search = async_rt_connection.search_assets()
+    items = [item async for item in search]
+    assert items
+
+    search = async_rt_connection.search_assets(
+        1, [{'field': 'Name', 'value': asset_name_new}], 
query_format={'fields': 'Owner', 'fields[Owner]': 'Name'}
+    )
     items = [item async for item in search]
     assert len(items) == 1
-    assert items[0]["Status"] == "new"
+    assert items[0]['Owner']['Name'] == 'Nobody'
+    assert 'Status' not in items[0]

Reply via email to