Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-fritzconnection for 
openSUSE:Factory checked in at 2024-10-30 17:36:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-fritzconnection (Old)
 and      /work/SRC/openSUSE:Factory/.python-fritzconnection.new.2020 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-fritzconnection"

Wed Oct 30 17:36:28 2024 rev:10 rq:1219189 version:1.14.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-fritzconnection/python-fritzconnection.changes
    2024-01-15 22:19:10.382720603 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-fritzconnection.new.2020/python-fritzconnection.changes
  2024-10-30 17:37:21.279591961 +0100
@@ -1,0 +2,7 @@
+Tue Oct 29 21:19:51 UTC 2024 - Dirk Müller <dmuel...@suse.com>
+
+- update to 1.14.0:
+  * support added for Python 3.13
+  * New FritzTopology module: represents the mesh-topology graph.
+
+-------------------------------------------------------------------

Old:
----
  fritzconnection-1.13.2.tar.gz

New:
----
  fritzconnection-1.14.0.tar.gz

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

Other differences:
------------------
++++++ python-fritzconnection.spec ++++++
--- /var/tmp/diff_new_pack.SyxZjU/_old  2024-10-30 17:37:22.823656642 +0100
+++ /var/tmp/diff_new_pack.SyxZjU/_new  2024-10-30 17:37:22.823656642 +0100
@@ -18,7 +18,7 @@
 
 %global pythons python3
 Name:           python-fritzconnection
-Version:        1.13.2
+Version:        1.14.0
 Release:        0
 Summary:        A Python module to talk to a AVM fritzbox
 License:        MIT

++++++ fritzconnection-1.13.2.tar.gz -> fritzconnection-1.14.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/.gitignore 
new/fritzconnection-1.14.0/.gitignore
--- old/fritzconnection-1.13.2/.gitignore       2023-09-17 14:58:30.000000000 
+0200
+++ new/fritzconnection-1.14.0/.gitignore       2024-08-12 17:58:36.000000000 
+0200
@@ -16,6 +16,7 @@
 bin
 build/*
 dist/*
+docs/requirements.txt
 fritzconnection.egg-info/*
 include
 lib_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/CONTRIBUTING.md 
new/fritzconnection-1.14.0/CONTRIBUTING.md
--- old/fritzconnection-1.13.2/CONTRIBUTING.md  2023-09-17 14:58:30.000000000 
+0200
+++ new/fritzconnection-1.14.0/CONTRIBUTING.md  2024-08-12 17:58:36.000000000 
+0200
@@ -1,21 +1,21 @@
 # Contributing
 
-Thank you for taking your time to contribute to this project.
+Thank you for taking time to contribute to this project.
 The project is not large enough to need a lot of rules, but here are some 
guidelines:
 
 
 ## Issues
 
-In case you think that something is not working as expected or you have a new 
idea, feel free to create an issue. Especially if you want to provide a 
pull-request, it is a good idea to create an issue first. Please also keep in 
mind that issues are not for support. For support try 'discussions' (but please 
keep in mind that this is not a commercial product, so you may get support or 
not).
+In case you think that something is not working as expected or you have a new 
idea, feel free to create an issue. Especially if you want to provide a 
pull-request, it is a good idea to create an issue first. Please also keep in 
mind that issues are not for support. For support try 'discussions' (please 
keep in mind that this is not a commercial product, so you may get support or 
not).
 
 
 ## Pull requests
 
-In general pull requests are welcome. But please create an issue first before 
putting too much work into a pull request that may not get merged at the end, 
for whatever reason. An issue can help to clarify points of view and motivation.
+In general pull requests are welcome. Please create an issue first before 
putting too much work into a pull request that may not get merged at the end, 
for whatever reason. An issue can help to clarify points of view and motivation.
 
 For pull requests there is a golden rule: **keep them small**. Smaller pull 
requests are easier to review and easier to merge – especially in cases when 
not every part of a larger changeset should get merged and has to get 
modifications.
 
-Please avoid to just change the formatting. The result is most often nothing 
else than git-diff pollution. This is especially true for `black` – this 
project startet before `black` (or `blue`). It is ok to use `black` for 
modified code snippets, but not for a module.
+Please avoid to just change the formatting. The result is most often nothing 
else than git-diff pollution. This is especially true for `black` (or `blue` or 
the corresponding modes in `ruff`) – this project startet before `black`. It 
is ok to use these tools for modified code snippets, but not for a module.
 
 
 ## Status of core and lib
@@ -32,7 +32,7 @@
 - In general the style guide is PEP 8.
 - Recommended maximum line length is 80 something.
 - Use proper names: i.e. an atrribute for `attributes` or `properties`, a verb 
for `callables`.
-- Try to avoid `black` (see "Pull requests").
+- Avoid `black` (see "Pull requests").
 
 
 ## Comments
@@ -44,11 +44,23 @@
 
 This project started long ago without type hints. Keep it that way. The 
project is small enough, so there is no real benefit. Also some parts of the 
code are really dynamic (one strength of Python), where type-hints can lead to 
a nightmare. The place for types in this project are in the comments. 
Readability counts!
 
-Update: since version 1.13.0 type hints are introduced for the public API (and 
only there). Type-hints must be backward compatible for the oldest supported 
python-version. For type-checking `mypy` is used.
+Update: since version `1.13.0` type hints are introduced for the public API 
(and only there). Type-hints must be backward compatible for the oldest 
supported python-version. For type-checking `mypy` is used.
 
 
 ## Tests
 
-This project comes with tests. The test framework is tox/pytest. For 
development run `pip install -r requirements_test.txt` to install both 
libraries. As far as possible, the code should get tests. Before committing run 
the tests with tox.
+This project comes with tests. Since version `1.13.0` `nox` is used for 
testautomation (`https://nox.thea.codes/en/stable/index.html`). `nox` should 
get installed separately. In the same environment where `nox` has been 
installed also `ruff` must be installed because the `noxfile` makes use of 
`ruff` as external module for linting.
 
-Update: since version 1.13.0 `nox` has been introduced for testing. For `nox` 
there is no need for the `requirements_test.txt` file.
+After installation the sessions from the `noxfile.py` can be used like:
+
+- `nox -s test`: run the tests with all supported python versions. The 
supported versions must be installed on the local development system and must 
be callable like `python3.11` or `python3.12`. Currently python `3.7` and newer 
are supported (no new language features are used, so backward compatibility is 
cheap). You can run `nox -s test-3.11` to run the tests with a single python 
version. This can be handy for development running the test for all versions 
later on.
+
+- `nox -s test_router`: run all tests making a connection to the router. These 
tests are taking much more time and can fail because of connection errors. In 
case of a connection error run the tests again - chances are good the error was 
temporarly and gone and there are no real bugs. In all other cases: fix it. A 
test run with a defined python version (here `3.11`) can get started with `nox 
-s test_router-3.11`.
+
+- `nox -s test_all`: run all tests including the time consuming router-tests.
+
+- `nox -s check`: run `ruff` to `lint` the code.
+
+- `nox -s mypy`: apply a mypy check.
+
+- `nox -s sphinx`:  make a local build of the documentation.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/README.md 
new/fritzconnection-1.14.0/README.md
--- old/fritzconnection-1.13.2/README.md        2023-09-17 14:58:30.000000000 
+0200
+++ new/fritzconnection-1.14.0/README.md        2024-08-12 17:58:36.000000000 
+0200
@@ -53,7 +53,7 @@
 
 ### Caching
 
-On instanciation FritzConnection has to inspect the model-specific router-API. 
This causes a lot of network requests and can take some seconds. To avoid this 
FritzConnection provides a cache that can get activated by the `use_cache` 
parameter:
+On instantiation FritzConnection has to inspect the model-specific router-API. 
This causes a lot of network requests and can take some seconds. To avoid this 
FritzConnection provides a cache that can get activated by the `use_cache` 
parameter:
 
 ```
     fc = FritzConnection(..., use_cache=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/docs/requirements.local.in 
new/fritzconnection-1.14.0/docs/requirements.local.in
--- old/fritzconnection-1.13.2/docs/requirements.local.in       1970-01-01 
01:00:00.000000000 +0100
+++ new/fritzconnection-1.14.0/docs/requirements.local.in       2024-08-12 
17:58:36.000000000 +0200
@@ -0,0 +1,3 @@
+Sphinx==5.1.1
+sphinx-rtd-theme==1.2.2
+furo==2023.3.27
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/docs/requirements.txt 
new/fritzconnection-1.14.0/docs/requirements.txt
--- old/fritzconnection-1.13.2/docs/requirements.txt    2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/docs/requirements.txt    2024-08-12 
17:58:36.000000000 +0200
@@ -1,78 +1,69 @@
 #
-# This file is autogenerated by pip-compile with Python 3.11
+# This file is autogenerated by pip-compile with Python 3.12
 # by the following command:
 #
-#    pip-compile --strip-extras docs/requirements.in
+#    pip-compile --output-file=docs/requirements.txt --strip-extras 
docs/requirements.local.in
 #
-alabaster==0.7.13
+alabaster==0.7.16
     # via sphinx
-babel==2.12.1
+babel==2.16.0
     # via sphinx
-beautifulsoup4==4.12.2
+beautifulsoup4==4.12.3
     # via furo
-certifi==2023.7.22
+certifi==2024.7.4
     # via requests
-charset-normalizer==3.2.0
+charset-normalizer==3.3.2
     # via requests
 docutils==0.18.1
     # via
     #   sphinx
     #   sphinx-rtd-theme
-fritzconnection==1.13.1
-    # via -r docs/requirements.in
 furo==2023.3.27
-    # via -r docs/requirements.in
-idna==3.4
+    # via -r docs/requirements.local.in
+idna==3.7
     # via requests
 imagesize==1.4.1
     # via sphinx
-jinja2==3.1.2
+jinja2==3.1.4
     # via sphinx
-markupsafe==2.1.3
+markupsafe==2.1.5
     # via jinja2
-packaging==23.1
+packaging==24.1
     # via sphinx
-pygments==2.16.1
+pygments==2.18.0
     # via
     #   furo
     #   sphinx
-requests==2.31.0
-    # via
-    #   fritzconnection
-    #   sphinx
+requests==2.32.3
+    # via sphinx
 snowballstemmer==2.2.0
     # via sphinx
-soupsieve==2.4.1
+soupsieve==2.5
     # via beautifulsoup4
 sphinx==5.1.1
     # via
-    #   -r docs/requirements.in
+    #   -r docs/requirements.local.in
     #   furo
     #   sphinx-basic-ng
     #   sphinx-rtd-theme
-    #   sphinxcontrib-applehelp
-    #   sphinxcontrib-devhelp
-    #   sphinxcontrib-htmlhelp
     #   sphinxcontrib-jquery
-    #   sphinxcontrib-qthelp
-    #   sphinxcontrib-serializinghtml
 sphinx-basic-ng==1.0.0b2
     # via furo
 sphinx-rtd-theme==1.2.2
-    # via -r docs/requirements.in
-sphinxcontrib-applehelp==1.0.7
+    # via -r docs/requirements.local.in
+sphinxcontrib-applehelp==2.0.0
     # via sphinx
-sphinxcontrib-devhelp==1.0.5
+sphinxcontrib-devhelp==2.0.0
     # via sphinx
-sphinxcontrib-htmlhelp==2.0.4
+sphinxcontrib-htmlhelp==2.1.0
     # via sphinx
 sphinxcontrib-jquery==4.1
     # via sphinx-rtd-theme
 sphinxcontrib-jsmath==1.0.1
     # via sphinx
-sphinxcontrib-qthelp==1.0.6
+sphinxcontrib-qthelp==2.0.0
     # via sphinx
-sphinxcontrib-serializinghtml==1.1.8
+sphinxcontrib-serializinghtml==2.0.0
     # via sphinx
-urllib3==2.0.4
+urllib3==2.2.2
     # via requests
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/docs/sources/getting_started.rst 
new/fritzconnection-1.14.0/docs/sources/getting_started.rst
--- old/fritzconnection-1.13.2/docs/sources/getting_started.rst 2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/docs/sources/getting_started.rst 2024-08-12 
17:58:36.000000000 +0200
@@ -60,7 +60,7 @@
       -e [ENCRYPT], --encrypt [ENCRYPT]
                             Flag: use secure connection (TLS)
       -x, --use-cache       Flag: use api cache
-                            (speed-up subsequent instanciations)
+                            (speed-up subsequent instantiations)
       -y, --suppress-cache-verification
                             Flag: suppress cache verification, implies -x
       --cache-format [CACHE_FORMAT]
@@ -215,9 +215,9 @@
 At first an instance of `FritzConnection` must be created. There can be a 
short delay doing this, because fritzconnection has to do a lot of 
communication with the router to get the router-specific API.
 
 .. note ::
-    A FritzConnection instance can be **reused** for all further 
`call_action()` calls (and also `call_http()` calls) **without side-effects**. 
For a single device (i.e. the router) an application needs just one instance. 
Because instanciation can be expensive (time consuming), having a single 
instance can save memory and speed up things.
+    A FritzConnection instance can be **reused** for all further 
`call_action()` calls (and also `call_http()` calls) **without side-effects**. 
For a single device (i.e. the router) an application needs just one instance. 
Because instantiation can be expensive (time consuming), having a single 
instance can save memory and speed up things.
 
-    Update: with the introduction of the `api-cache` in version `1.10` 
instanciation is much more faster than before. However, reusing an instance is 
still a good idea.
+    Update: with the introduction of the `api-cache` in version `1.10` 
instantiation is much more faster than before. However, reusing an instance is 
still a good idea.
 
 The method `call_action` takes two required arguments: the service- and the 
action-name as strings. In case that a service is unknown (because of a typo or 
incompatible router model) fritzconnection will raise a `FritzServiceError`. If 
the service is known, but not the action, then a `FritzActionError` gets raised.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/docs/sources/library_modules.rst 
new/fritzconnection-1.14.0/docs/sources/library_modules.rst
--- old/fritzconnection-1.13.2/docs/sources/library_modules.rst 2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/docs/sources/library_modules.rst 2024-08-12 
17:58:36.000000000 +0200
@@ -287,6 +287,36 @@
     :members:
 
 
+FritzTopology
+-------------
+
+Module to access the current Network-Topology information. This can be a 
cyclic graph with the devices as nodes. The devices do have interfaces and the 
interfaces provide links to other the interfaces of other devices. Links do 
have attributes like the current throughput.
+
+The following example prints an overview of a topology: ::
+
+    from fritzconnection import FritzConnection
+    from fritzconnection.lib.fritztopology import FritzMeshTopology
+
+    # assume user and password are read from the environment:
+    fc = FritzConnection(address="192.168.178.1")
+
+    # create a topology-instance
+    fm = FritzMeshTopology(fc=fc)
+
+    # read the topology data (can take some time)
+    fm.load_topology()
+
+    # print the instance:
+    print(fm)
+
+
+FritzTopology API
+.................
+
+.. automodule:: fritzconnection.lib.fritztopology
+    :members:
+
+
 FritzWLAN
 ---------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/docs/sources/version_history.rst 
new/fritzconnection-1.14.0/docs/sources/version_history.rst
--- old/fritzconnection-1.13.2/docs/sources/version_history.rst 2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/docs/sources/version_history.rst 2024-08-12 
17:58:36.000000000 +0200
@@ -4,6 +4,28 @@
 ===============
 
 
+1.14.0 - 2024-08-12
+-------------------
+
+- support added for Python 3.13
+- New FritzTopology module: represents the mesh-topology graph.
+- FritzWLAN:
+
+  - internal function `_get_beacon_security()` removed and substituted with a 
mapping. (#224)
+  - added ability to output QR code as string stream with ANSI or unicode. 
(#223)
+  - new property `is_hidden` for wlan ssid. Internal use of this property to 
handle hidden SSID in QR codes. Parameter `hidden` removed from 
`FritzWLAN.get_wifi_qr_code()`. (#221)
+  - bugfix: fixed potential password leak in QR code for an unencrypted 
connection. (#225, #226)
+
+- FritzHomeAutomation:
+
+  - bugfix: converting data from actors which are reporting missing data as 
non integer values. Missing data are now represented by `None`. (#215)
+
+- bugfix: some devices may not return system-information the propper way, 
causing errors on the cli output. In these cases the system-information will 
get ignored. (#214)
+- documentation: some typos corrected. (#202, #204)
+- testing: `tox.ini` removed because of change to `nox`. Change from `pylint` 
to `ruff` for linting.
+- deprecation: use of the `json` cache-format is discouraged. Use the default 
pickle-format instead. The highly dynamic TR-064 parser may get an ouverhaul in 
the future and to reduce the complexity of the parser the support of `json` for 
caching will be removed.
+
+
 1.13.2 - 2023-09-17
 -------------------
 
@@ -117,7 +139,7 @@
 
 - FritzConnection:
 
-  - API cache integration added: for faster start up times the router API can 
optional get saved in a cache-file. This can save up to several seconds 
run-time on instanciation.
+  - API cache integration added: for faster start up times the router API can 
optional get saved in a cache-file. This can save up to several seconds 
run-time on instantiation.
 
 - FritzHosts:
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/fritzconnection/__init__.py 
new/fritzconnection-1.14.0/fritzconnection/__init__.py
--- old/fritzconnection-1.13.2/fritzconnection/__init__.py      2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/__init__.py      2024-08-12 
17:58:36.000000000 +0200
@@ -10,7 +10,10 @@
 https://fritzconnection.readthedocs.io/
 """
 
-__version__ = "1.13.2"
+# unused shortcut import are intended:
+# ruff: noqa: F401
+
+__version__ = "1.14.0"
 
 # import shortcuts
 from .core.fritzconnection import FritzConnection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/cli/fritzinspection.py 
new/fritzconnection-1.14.0/fritzconnection/cli/fritzinspection.py
--- old/fritzconnection-1.13.2/fritzconnection/cli/fritzinspection.py   
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/cli/fritzinspection.py   
2024-08-12 17:58:36.000000000 +0200
@@ -87,11 +87,13 @@
         """
         print()
         system_info = self.fc.device_manager.system_info
-        print(
-            f"system : {system_info[-1]}\n"
-            f"build  : {system_info[-2]}\n"
-            f"hw-code: {system_info[0]}"
-        )
+        # some devices may return None as system_info
+        if system_info:
+            print(
+                f"system : {system_info[-1]}\n"
+                f"build  : {system_info[-2]}\n"
+                f"hw-code: {system_info[0]}"
+            )
         now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
         print(f"Report date: {now}")
         for service_name, service in self.fc.services.items():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/fritzconnection/cli/utils.py 
new/fritzconnection-1.14.0/fritzconnection/cli/utils.py
--- old/fritzconnection-1.13.2/fritzconnection/cli/utils.py     2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/cli/utils.py     2024-08-12 
17:58:36.000000000 +0200
@@ -35,13 +35,13 @@
 
 
 def print_common_exception_message(error_object):
-        print(error_object)
-        print(
-            "\nSeems you forgot to provide the user and/or the password."
-            "\nYou can provide these with the flag -u and -p or store them"
-            "\nin the environment as FRITZ_USERNAME and FRITZ_PASSWORD."
-            "\n(Environment changes will get recognized by new processes.)\n"
-        )
+    print(error_object)
+    print(
+        "\nSeems you forgot to provide the user and/or the password."
+        "\nYou can provide these with the flag -u and -p or store them"
+        "\nin the environment as FRITZ_USERNAME and FRITZ_PASSWORD."
+        "\n(Environment changes will get recognized by new processes.)\n"
+    )
 
 
 def get_instance(cls, args):
@@ -86,7 +86,7 @@
                         action="store_true",
                         dest='use_cache',
                         help='Flag: use api cache (e[x]cellerate: speed-up 
subsequent '
-                             'instanciations)'
+                             'instantiations)'
                         )
     parser.add_argument('-y', '--suppress-cache-verification',
                         action='store_false',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/core/fritzconnection.py 
new/fritzconnection-1.14.0/fritzconnection/core/fritzconnection.py
--- old/fritzconnection-1.13.2/fritzconnection/core/fritzconnection.py  
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/core/fritzconnection.py  
2024-08-12 17:58:36.000000000 +0200
@@ -152,7 +152,7 @@
     .. versionadded:: 1.6
 
     The flag `use_cache` activates caching (default `False`). Caching
-    can speed up instanciation significantly. The cached data are
+    can speed up instantiation significantly. The cached data are
     specific for the router ip, the router model and the installed
     FritzOS version. Multiple devices in the network can have separate
     cache-fies and can get used in parallel. By default the cache files
@@ -219,7 +219,7 @@
 
         `use_cache` is a boolean whether a cache should get used for the
         router api data. By default the api data are loaded from the
-        router at instanciation time what can take several seconds to
+        router at instantiation time what can take several seconds to
         complete. `cache_directory` is the path to the directory storing
         the cached data. By default this is a folder named
         '.fritzconnection' in the users home-directory. `cache_format`
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/core/fritzhttp.py 
new/fritzconnection-1.14.0/fritzconnection/core/fritzhttp.py
--- old/fritzconnection-1.13.2/fritzconnection/core/fritzhttp.py        
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/core/fritzhttp.py        
2024-08-12 17:58:36.000000000 +0200
@@ -1,7 +1,7 @@
 """
 fritzhttp.py
 
-Access the AVM Fritz!Box AHA-HTTP-Inferface
+Access the AVM Fritz!Box AHA-HTTP-Interface
 """
 # This module is part of the FritzConnection package.
 # https://github.com/kbr/fritzconnection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/lib/fritzhomeauto.py 
new/fritzconnection-1.14.0/fritzconnection/lib/fritzhomeauto.py
--- old/fritzconnection-1.13.2/fritzconnection/lib/fritzhomeauto.py     
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/lib/fritzhomeauto.py     
2024-08-12 17:58:36.000000000 +0200
@@ -395,7 +395,16 @@
                 if key == "datatime":
                     value = datetime.datetime.fromtimestamp(value)  # type: 
ignore
                 content[key] = value
-            content["data"] = list(map(int, stats.text.split(",")))  # type: 
ignore
+            # convert the csv-list of returned values from text to int.
+            # on missing data dashes (-) may get returned.
+            # this get catched and missing data are represented as `None`.
+            content["data"] = []
+            for item in stats.text.split(","):  # type: ignore
+                try:
+                    value = int(item)  # type: ignore
+                except ValueError:
+                    value = None  # type: ignore
+                content["data"].append(value)
             elements[element.tag] = content
         return elements
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/lib/fritzhosts.py 
new/fritzconnection-1.14.0/fritzconnection/lib/fritzhosts.py
--- old/fritzconnection-1.13.2/fritzconnection/lib/fritzhosts.py        
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/lib/fritzhosts.py        
2024-08-12 17:58:36.000000000 +0200
@@ -130,7 +130,7 @@
             )
         return result
 
-    def get_mesh_topology(self, raw=False) -> dict:
+    def get_mesh_topology(self, raw=False) -> dict | str:
         """
         Returns information about the mesh network topology. If `raw` is
         `False` the topology gets returned as a dictionary with a list
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/lib/fritztopology.py 
new/fritzconnection-1.14.0/fritzconnection/lib/fritztopology.py
--- old/fritzconnection-1.13.2/fritzconnection/lib/fritztopology.py     
1970-01-01 01:00:00.000000000 +0100
+++ new/fritzconnection-1.14.0/fritzconnection/lib/fritztopology.py     
2024-08-12 17:58:36.000000000 +0200
@@ -0,0 +1,299 @@
+"""
+Module to access to the Mesh Topology
+See https://avm.de/service/schnittstellen/ "Mesh-Topologie"
+"""
+# This module is part of the FritzConnection package.
+# https://github.com/kbr/fritzconnection
+# License: MIT (https://opensource.org/licenses/MIT)
+# Authors: Klaus Bremer
+
+# code partly to dynamic for a useful mypy run:
+# mypy: disable-error-code="attr-defined"
+
+from __future__ import annotations
+
+from .fritzhosts import FritzHosts
+from .fritzbase import AbstractLibraryBase
+
+
+class Connection:
+    """
+    Represents a connection from one device to another device.
+    This is basically a convenience wrapper for InterfaceLink
+    exposing the devices and some instance attributes in a more
+    comfortable way.
+
+    self.source is the origin device
+    self.target is the connected device
+    self.type is the connection type (like "WLAN")
+    self.state is the connection state (like "DISCONNECTED", "CONNECTED")
+
+    self.max_rx, max_tx, cur_rx and cur_tx are the maximum and
+    current throughput data rates in kbit/sec.
+
+    tx is the upload-throughput from source to target, even if the
+    source is represented by the interface-link node 2.
+    """
+
+    def __init__(self, source, target, interface_link):
+        self.source = source
+        self.target = target
+        self.interface_link = interface_link
+
+    @property
+    def type(self) -> str | None:
+        return self._get_interface_attribute("type")
+
+    @property
+    def state(self) -> str | None:
+        return self._get_interface_attribute("state")
+
+    @property
+    def max_rx(self) -> int | None:
+        return self._get_transfer_rate("max_data_rate_rx")
+
+    @property
+    def max_tx(self) -> int | None:
+        return self._get_transfer_rate("max_data_rate_tx")
+
+    @property
+    def cur_rx(self) -> int | None:
+        return self._get_transfer_rate("cur_data_rate_rx")
+
+    @property
+    def cur_tx(self) -> int | None:
+        return self._get_transfer_rate("cur_data_rate_tx")
+
+    def _get_transfer_rate(self, name):
+        """
+        Adapt the rate to the link direction: 'tx' is the transfer rate
+        from node 1 to node 2 which is ok, if node 1 represents the
+        'source' node. In case it is the other way around the commands
+        'tx' and 'rx' must get exchanged.
+        """
+        if self.interface_link.source_index == 2:
+            name, direction = name.rsplit("_", 1)
+            direction = "rx" if direction == "tx" else "tx"
+            name = f"{name}_{direction}"
+        return self._get_interface_attribute(name)
+
+    def _get_interface_attribute(self, name, default=None):
+        return getattr(self.interface_link, name, default)
+
+
+class InterfaceLink:
+    """
+    Represents a node-link from an Interface of a device
+    to another Interface of another device.
+    (These are the vertexes of the mesh.)
+
+    Instance attributes are all elements described as "node_links.properties"
+    in the AVM documentation and accessible by their names, i.e. like
+    'self.last_connected' in case the UNIX timestamp is requested.
+    """
+
+    def __init__(self, data: dict, interface: Interface):
+        self.__dict__.update(data)
+        self.interface = interface
+        # The starting point is assumed to be the device, which is
+        # the owner of self.interface.
+        if self.node_interface_1_uid == self.interface.uid:
+            self.source_index = 1
+            self.target_index = 2
+        else:
+            self.source_index = 2
+            self.target_index = 1
+
+    def __repr__(self) -> str:
+        return f"InterfaceLink:  {self.uid:<8}target-interface:  
{self.node_interface_2_uid}"
+
+    def __str__(self) -> str:
+        connection = Connection(self.source, self.target, self)
+        if connection.cur_tx is not None:
+            throughput = connection.cur_tx // 1000  # from kB/s to MB/s
+        else:
+            throughput = None  # not known
+        return f"from {self.source.name} to {self.target.name} (-> 
{throughput} MBit/s)"
+
+    @property
+    def source(self) -> Device:
+        return self._get_connected_device(target=False)
+
+    @property
+    def target(self) -> Device:
+        return self._get_connected_device(target=True)
+
+    def get_connection(self) -> Connection:
+        """
+        Returns a connection instance describing a connection
+        from one device to another device.
+        """
+        return Connection(self.source, self.target, self)
+
+    def _get_connected_device(self, target=True):
+        """
+        Returns the device connected by this link,
+        either the target or source device.
+        """
+        index = self.target_index if target else self.source_index
+        device_id = getattr(self, f"node_{index}_uid")
+        return self.interface.device.mesh.get_device_by_id(device_id)
+
+
+class Interface:
+    """
+    An interface represents a physical infrastructure of a device
+    to connect with other devices.
+    An interface is part of a device and able to connect to another
+    interface of another device.
+    A connection is represented by the InterfaceLink class.
+    """
+
+    def __init__(self, data: dict, device: Device):
+        self.__dict__.update(data)
+        self.interface_links = [
+            InterfaceLink(link, self) for link in self.node_links
+        ]
+        self.device = device
+
+    def __repr__(self) -> str:
+        representation = (
+            f"Interface:  mac={self.mac:<20}"
+            f"uid={self.uid:<8}"
+            f"type={self.type:<8}"
+        )
+        if self.name:
+            representation = f"{representation}name={self.name}"
+        return representation
+
+    def __str__(self) -> str:
+        interface = repr(self)
+        if self.interface_links:
+            space = " " * 8
+            links = f"\n{space}".join(str(link) for link in 
self.interface_links)
+            interface = f"{interface}\n{space}{links}"
+        return interface
+
+    @property
+    def mac(self) -> str:
+        """mac address of the interface"""
+        return self.mac_address
+
+    def get_connections(self):
+        """
+        Returns a list of Connection objects describing all devices
+        connected by this interface.
+        """
+        return [link.get_connection() for link in self.interface_links]
+
+
+class Device:
+    """
+    A Device represents a physical item in the mesh, like a router,
+    repeater, a laptop, a cell phone and other devices.
+    (This is a node of the mesh.)
+
+    Instance attributes created dynamical according to the
+    AVM mesh topology schema: https://avm.de/service/schnittstellen/
+
+    For convenience there are some properties and methods to access
+    the data in a more mnemonic way.
+    """
+
+    def __init__(self, data: dict, mesh: FritzMeshTopology):
+        self.__dict__.update(data)
+        self.interfaces = [
+            Interface(interface_data, self)
+            for interface_data in self.node_interfaces
+        ]
+        self.mesh = mesh
+
+    def __repr__(self) -> str:
+        return f"Device:  mac={self.mac:<20}uid={self.uid:<8}{self.name}"
+
+    def __str__(self) -> str:
+        representation = repr(self)
+        space = " "  * 4
+        interfaces = f"\n{space}".join(str(interface) for interface in 
self.interfaces)
+        return f"{representation}\n{space}{interfaces}"
+
+    @property
+    def name(self) -> str:
+        return self.device_name
+
+    @property
+    def model(self) -> str:
+        return self.device_model
+
+    @property
+    def vendor(self) -> str:
+        return self.device_manufacturer
+
+    @property
+    def mac(self) -> str:
+        return self.device_mac_address
+
+    def get_connections(self):
+        """
+        Returns a list of Connection objects describing all
+        devices connected to this device.
+        """
+        connections = []
+        for interface in self.interfaces:
+            connections.extend(interface.get_connections())
+        return connections
+
+
+class FritzMeshTopology(AbstractLibraryBase):
+    """
+    Represents the mesh topology with nodes representing the devices.
+
+    """
+
+    def __init__(self, fc=None, *args, **kwargs):
+        super().__init__(fc, *args, **kwargs)
+        self._fritzhosts = FritzHosts(self.fc)
+        self.topology = {}
+        self.nodes = {}
+
+    def __repr__(self) -> str:
+        return (
+            f"{self.__class__.__name__}: "
+            f"schema_version={self.schema_version}, "
+            f"registered Devices: {self.number_of_devices}"
+        )
+
+    def __str__(self) -> str:
+        nodes = "\n\n".join(str(device) for device in self.devices)
+        return f"{repr(self)}\n{nodes}"
+
+    @property
+    def schema_version(self) -> str:
+        return self.topology.get("schema_version", "unknown")
+
+    @property
+    def number_of_devices(self) -> int:
+        return len(self.nodes)
+
+    @property
+    def devices(self) -> list[Device]:
+        return list(self.nodes.values())
+
+    def get_device_by_id(self, uid: str) -> Device:
+        """
+        Returns a device by id. The id is the node-uid like "n-1".
+        The node-uids are fixed to devices after calling
+        `load_topology()` but may change if `load_topology()` gets
+        called again.
+        """
+        return self.nodes[uid]
+
+    def load_topology(self):
+        """
+        Load the topology from the router.
+        """
+        self.topology = self._fritzhosts.get_mesh_topology(raw=False)
+        self.nodes = {
+            node["uid"]: Device(node, self)
+            for node in self.topology.get("nodes", ())
+        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/lib/fritzwlan.py 
new/fritzconnection-1.14.0/fritzconnection/lib/fritzwlan.py
--- old/fritzconnection-1.13.2/fritzconnection/lib/fritzwlan.py 2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/lib/fritzwlan.py 2024-08-12 
17:58:36.000000000 +0200
@@ -28,52 +28,28 @@
 # important: don't set an extension number here:
 SERVICE = 'WLANConfiguration'
 DEFAULT_PASSWORD_LENGTH = 12
-WPA_SECURITY = 'WPA'
-NO_PASS = 'nopass'
+_QR_SECURITY_WPA = 'WPA'
+_QR_SECURITY_WEP  = 'WEP'
+_QR_SECURITY_NO_PASS = 'nopass'
+_BEACONTYPE_TO_QR_SECURITY = {
+    'WPA': _QR_SECURITY_WPA,
+    '11i': _QR_SECURITY_WPA,
+    'WPAand11i': _QR_SECURITY_WPA,
+    'WPA3': _QR_SECURITY_WPA,
+    '11iandWPA3': _QR_SECURITY_WPA,
+    'Basic': _QR_SECURITY_WEP,
+    'OWE': _QR_SECURITY_NO_PASS,
+    'OWETrans': _QR_SECURITY_NO_PASS,
+    'None': _QR_SECURITY_NO_PASS,
+}
 
-POSSIBLE_BEACON_TYPES_KEY = "NewX_AVM-DE_PossibleBeaconTypes"
 
-
-def _get_beacon_security(instance, security):
-    """
-    Returns the beacon-security as a string based on the security
-    argument. Possible return values are 'nopass' and 'WPA'. If the
-    security is None or an empty string, the function tries to find the
-    proper security setting ('nopass'|'WPA'). If security is neither
-    None nor an empty string, the value is returned as is.
-
-    This function is not intended to get called directly.
-
-    .. versionadded:: 1.10
-    """
-    if not security:
-        security = NO_PASS
-        info = instance.get_info()
-        beacontype = info["NewBeaconType"]
-        # check for the POSSIBLE_BEACON_TYPES_KEY argument
-        # as older models may not provide it:
-        if POSSIBLE_BEACON_TYPES_KEY in info:
-            beacontypes = set(info[POSSIBLE_BEACON_TYPES_KEY].split(","))
-            beacontypes -= set(('None', 'OWETrans'))
-            if beacontype in beacontypes:
-                security = WPA_SECURITY
-        else:
-            # dealing with an older model
-            # assuming at least providing WPA security
-            # (unable to test for WEP because of missing hardware)
-            if beacontype != "None":
-                security = WPA_SECURITY
-    return security
-
-
-def _get_wifi_qr_code(instance, kind='svg',
-                     security=None, hidden=False,
-                     scale=4):
+def _get_wifi_qr_code(instance, kind='svg', security=None, scale=4, border=0):
     """
     Returns a file-like object providing a bytestring representing a
     qr-code for wlan access. `instance` is a FritzWLAN or FritzGuestWLAN
     instance. `kind` describes the type of the qr-code. Supported types
-    are: 'svg', 'png' and 'pdf'. Default is 'svg'.
+    are: 'svg', 'png', 'pdf', 'text' and 'text-compact'. Default is 'svg'.
 
     This function is not intended to get called directly. Instead it is
     available as a method on FritzWLAN instances (as well as on
@@ -93,6 +69,11 @@
         with open('qr_code.png', 'wb') as fobj:
             fobj.write(stream.read())
 
+    The stream can be printed out if qr-code is represented as text: ::
+
+        stream = guest_wlan.get_wifi_qr_code(kind='text')
+        print(stream.getvalue())
+
     If `segno` is not installed the call will trigger an
     AttributeError when called on an instance and a NameError when
     called directly.
@@ -115,15 +96,24 @@
     .. versionadded:: 1.10
 
     """
-    stream = io.BytesIO()
-    security = _get_beacon_security(instance, security)
+    if not security:
+        # if beacon type is something unknown, assume a "no pass" connection:
+        security = _BEACONTYPE_TO_QR_SECURITY.get(instance.beacontype,
+                                                  _QR_SECURITY_NO_PASS)
+    password = instance.get_password() if security != _QR_SECURITY_NO_PASS 
else None
     qr_code = segno.helpers.make_wifi(
         ssid=instance.ssid,
-        password=instance.get_password(),
+        password=password,
         security=security,
-        hidden=hidden
+        hidden=instance.is_hidden
     )
-    qr_code.save(out=stream, kind=kind, scale=scale)
+    if kind in ['text', 'text-compact']:
+        stream = io.StringIO()
+        compact = kind != 'text'
+        qr_code.terminal(out=stream, border=border, compact=compact)
+    else:
+        stream = io.BytesIO()
+        qr_code.save(out=stream, kind=kind, scale=scale, border=border)
     stream.seek(0)
     return stream
 
@@ -204,6 +194,11 @@
         return self.get_info()['NewBeaconType']
 
     @property
+    def is_hidden(self) -> bool:
+        """Returns True if the SSID hidden (not advertised through beacons)."""
+        return not 
self._action('GetBeaconAdvertisement')['NewBeaconAdvertisementEnabled']
+
+    @property
     def channel(self) -> int:
         """The WLAN channel in use"""
         return self.channel_info()['NewChannel']
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_devices.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_devices.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_devices.py    
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_devices.py    
2024-08-12 17:58:36.000000000 +0200
@@ -1,10 +1,7 @@
 import json
 import os
-from io import StringIO
-from pathlib import Path
 
 import pytest
-from xml.etree import ElementTree as etree
 
 from ..core.devices import DeviceManager
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzconnection.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzconnection.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzconnection.py    
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzconnection.py    
2024-08-12 17:58:36.000000000 +0200
@@ -76,7 +76,7 @@
 def test_invalide_cache_format():
     mock = SimpleNamespace(address='192.168.178.1', port=49000)
     with pytest.raises(FritzConnectionException):
-        result = FritzConnection._get_cache_path(mock, None, 'weird')
+        _ = FritzConnection._get_cache_path(mock, None, 'weird')
 
 
 @pytest.mark.parametrize(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzhomeauto.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzhomeauto.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzhomeauto.py      
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzhomeauto.py      
2024-08-12 17:58:36.000000000 +0200
@@ -1,8 +1,4 @@
 
-import json
-import pathlib
-import pytest
-
 from ..lib.fritzhomeauto import HomeAutomationDevice
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzmonitor.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzmonitor.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzmonitor.py       
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzmonitor.py       
2024-08-12 17:58:36.000000000 +0200
@@ -307,7 +307,7 @@
     mock_socket = MockReconnectFailSocket(timeouts=timeouts)
     fm = FritzMonitor()
     fm.mock_socket = mock_socket
-    socket = fm._get_connected_socket()  # make initional connection
+    _ = fm._get_connected_socket()  # make initional connection
     result = fm._reconnect_socket(
         max_reconnect_delay=0.001, reconnect_tries=tries
     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzwlan.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzwlan.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_fritzwlan.py  
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_fritzwlan.py  
2024-08-12 17:58:36.000000000 +0200
@@ -16,7 +16,6 @@
 else:
     OPENCV_NOT_AVAILABLE = False
 
-from fritzconnection.lib.fritzwlan import _get_beacon_security as 
get_beacon_security
 from fritzconnection.lib.fritzwlan import _get_wifi_qr_code as get_wifi_qr_code
 
 
@@ -121,31 +120,6 @@
         return self.mock_data["NewSSID"]
 
 
-@pytest.mark.parametrize(
-    "current_beacontype, security, expected_result", [
-        ('11i', None, 'WPA'),
-        ('11i', '', 'WPA'),
-        ('11i', 'WPA3', 'WPA3'),
-        ('WPAand11i', None, 'WPA'),
-        ('11iandWPA3', None, 'WPA'),
-        ('None', None, 'nopass'),
-        ('OWETrans', None, 'nopass'),
-    ]
-)
-def test_get_beacon_security(current_beacontype, security, expected_result):
-    """
-    Test for correct selection of "WPA" or "nopass" depending on the
-    WLAN-settings.
-    """
-    mock_data = {
-        'NewBeaconType': current_beacontype,
-        'NewX_AVM-DE_PossibleBeaconTypes': 'None,11i,WPAand11i,11iandWPA3'
-    }
-    instance = WLANConfigMock(mock_data)
-    result = get_beacon_security(instance, security)
-    assert result == expected_result
-
-
 @pytest.mark.skipif(OPENCV_NOT_AVAILABLE, reason="requires opencv")
 @pytest.mark.parametrize(
     "current_beacontype, security, expected_security", [
@@ -154,6 +128,7 @@
         ('11i', 'WPA3', 'WPA3'),
         ('WPAand11i', None, 'WPA'),
         ('11iandWPA3', None, 'WPA'),
+        ('Basic', None, 'WEP'),
         ('None', None, 'nopass'),
         ('OWETrans', None, 'nopass'),
     ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_functional.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_functional.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_functional.py 
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_functional.py 
2024-08-12 17:58:36.000000000 +0200
@@ -3,7 +3,6 @@
 """
 
 import pytest
-import requests
 
 from ..core.exceptions import (
     FritzConnectionException,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_processor.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_processor.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_processor.py  
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_processor.py  
2024-08-12 17:58:36.000000000 +0200
@@ -151,7 +151,7 @@
 
 def test_system_version_igd(igddesc_source):
     d = Description(igddesc_source)
-    assert d.system_version == None
+    assert d.system_version is None
 
 
 def test_system_version_tr64(tr64desc_source):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/fritzconnection-1.13.2/fritzconnection/tests/test_serialize.py 
new/fritzconnection-1.14.0/fritzconnection/tests/test_serialize.py
--- old/fritzconnection-1.13.2/fritzconnection/tests/test_serialize.py  
2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/fritzconnection/tests/test_serialize.py  
2024-08-12 17:58:36.000000000 +0200
@@ -4,8 +4,6 @@
 
 import json
 
-import pytest
-
 from fritzconnection.core.processor import (
     Action,
     Argument,
@@ -71,12 +69,12 @@
 JSON_RESULT_TEST_SERIALIZE_ACTION = f"""{{"name": "{DATA_ACTION_NAME}", 
"arguments": [{JSON_RESULT_TEST_SERIALIZE_ARGUMENT}, 
{JSON_RESULT_TEST_SERIALIZE_ARGUMENT}]}}"""
 
 # weird but f-strings substitute lists of strings different than json.dumps:
-JSON_RESULT_TEST_SERIALIZE_STATEVARIABLE = f"""{{"attributes": 
{{"allowed_values": """ + json.dumps(DATA_STATEVARIABLE_ALLOWED_VALUES) + f""", 
"dataType": "{DATA_STATEVARIABLE_DATATYPE}", "defaultValue": 
"{DATA_STATEVARIABLE_DEFAULTVALUE}", "name": "{DATA_STATEVARIABLE_NAME}"}}, 
"allowedValueRange": {JSON_RESULT_TEST_SERIALIZE_VALUERANGE}}}"""
+JSON_RESULT_TEST_SERIALIZE_STATEVARIABLE = f"""{{"attributes": 
{{"allowed_values": """ + json.dumps(DATA_STATEVARIABLE_ALLOWED_VALUES) + f""", 
"dataType": "{DATA_STATEVARIABLE_DATATYPE}", "defaultValue": 
"{DATA_STATEVARIABLE_DEFAULTVALUE}", "name": "{DATA_STATEVARIABLE_NAME}"}}, 
"allowedValueRange": {JSON_RESULT_TEST_SERIALIZE_VALUERANGE}}}"""  # noqa: F541
 JSON_RESULT_TEST_SERIALIZE_SCPD = f"""{{"actions": 
[{JSON_RESULT_TEST_SERIALIZE_ACTION}, {JSON_RESULT_TEST_SERIALIZE_ACTION}], 
"specVersion": {JSON_RESULT_TEST_SERIALIZE_SPECVERSION}, "state_variables": 
[{JSON_RESULT_TEST_SERIALIZE_STATEVARIABLE}, 
{JSON_RESULT_TEST_SERIALIZE_STATEVARIABLE}]}}"""
 JSON_RESULT_TEST_SERIALIZE_SERVICE = f"""{{"attributes": {{"SCPDURL": 
"{DATA_SERVICE_SCPDURL}", "controlURL": "{DATA_SERVICE_CONTROLURL}", 
"eventSubURL": "{DATA_SERVICE_EVENTSUBURL}", "serviceId": 
"{DATA_SERVICE_SERVICEID}", "serviceType": "{DATA_SERVICE_SERVICETYPE}"}}, 
"scpd": {JSON_RESULT_TEST_SERIALIZE_SCPD}}}"""
 JSON_RESULT_TEST_SERIALIZE_DEVICE_BASIC = f"""{{"attributes": {{"UDN": 
"{DATA_DEVICE_UDN}", "UPC": null, "deviceType": "{DATA_DEVICE_DEVICETYPE}", 
"friendlyName": "{DATA_DEVICE_FRIENDLYNAME}", "manufacturer": 
"{DATA_DEVICE_MANUFACTURER}", "manufacturerURL": 
"{DATA_DEVICE_MANUFACTURERURL}", "modelDescription": 
"{DATA_DEVICE_MODELDESCRIPTION}", "modelName": "{DATA_DEVICE_MODELNAME}", 
"modelNumber": "{DATA_DEVICE_MODELNUMBER}", "modelURL": 
"{DATA_DEVICE_MODELURL}", "presentationURL": 
"{DATA_DEVICE_PRESENTATIONURL}"}}"""
 JSON_RESULT_TEST_SERIALIZE_DEVICE_BASIC_SERVICE = f""", "services": 
[{JSON_RESULT_TEST_SERIALIZE_SERVICE}, 
{JSON_RESULT_TEST_SERIALIZE_SERVICE}]}}"""
-JSON_RESULT_TEST_SERIALIZE_DEVICE = JSON_RESULT_TEST_SERIALIZE_DEVICE_BASIC + 
f""", "devices": [], "services": []}}"""
+JSON_RESULT_TEST_SERIALIZE_DEVICE = JSON_RESULT_TEST_SERIALIZE_DEVICE_BASIC + 
f""", "devices": [], "services": []}}"""  # noqa: F541
 JSON_RESULT_TEST_SERIALIZE_DEVICE_WITH_SERVICES = 
JSON_RESULT_TEST_SERIALIZE_DEVICE_BASIC + ', "devices": []' + 
JSON_RESULT_TEST_SERIALIZE_DEVICE_BASIC_SERVICE
 
 # subdevices don't provide services and further devices
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/noxfile.py 
new/fritzconnection-1.14.0/noxfile.py
--- old/fritzconnection-1.13.2/noxfile.py       2023-09-17 14:58:30.000000000 
+0200
+++ new/fritzconnection-1.14.0/noxfile.py       2024-08-12 17:58:36.000000000 
+0200
@@ -1,7 +1,7 @@
 import nox
 
 
-PYTHON_TEST_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12")
+PYTHON_TEST_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13")
 PYTHON_DEVELOPMENT_VERSION = "3.11"
 
 
@@ -26,17 +26,15 @@
     session.run("pytest")
 
 
-@nox.session
-def lint(session):
-    session.install("-e", ".")
-    session.install("pylint")
-    session.run("pylint", "fritzconnection")
+@nox.session(name="check")
+def ruff_check(session):
+    session.run("ruff", "check", "fritzconnection", external=True)
 
 
 @nox.session
 def mypy(session):
     session.install("-e", ".")
-    session.install("mypy==1.4.1", "types-requests", "segno")
+    session.install("mypy==1.11.1", "types-requests", "segno")
     session.run("mypy", "fritzconnection/core/fritzconnection.py")
     session.run("mypy", "fritzconnection/core/fritzmonitor.py")
     session.run("mypy", "fritzconnection/lib")
@@ -46,7 +44,13 @@
 def sphinx(session):
     session.install("-e", ".")
     session.install("pip-tools==7.3.0")
-    session.run("pip-compile", "--strip-extras", "-q", "docs/requirements.in")
+    session.run(
+        "pip-compile",
+        "--strip-extras",
+        "-q",
+        "--output-file=docs/requirements.txt",
+        "docs/requirements.local.in"
+    )
     session.install("-r", "docs/requirements.txt")
     session.run("sphinx-build", "docs", "docs_out")
 
@@ -57,8 +61,15 @@
     session.run("python", "setup.py", "sdist", "bdist_wheel")
 
 
+@nox.session(name="check-twine", python=PYTHON_DEVELOPMENT_VERSION)
+def check_twine(session):
+    session.install("-e", ".")
+    session.install("twine")
+    session.run("twine", "check", "dist/*")
+
+
 @nox.session(name="upload-to-pypi", python=PYTHON_DEVELOPMENT_VERSION)
-def uppload_to_pypi(session):
+def upload_to_pypi(session):
     session.install("-e", ".")
     session.install("twine")
     session.run("twine", "upload", "dist/*")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/requirements_test.txt 
new/fritzconnection-1.14.0/requirements_test.txt
--- old/fritzconnection-1.13.2/requirements_test.txt    2023-09-17 
14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/requirements_test.txt    1970-01-01 
01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-pytest
-tox
-segno
-opencv-python-headless
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/setup.py 
new/fritzconnection-1.14.0/setup.py
--- old/fritzconnection-1.13.2/setup.py 2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/setup.py 2024-08-12 17:58:36.000000000 +0200
@@ -43,9 +43,10 @@
         "Programming Language :: Python :: 3.10",
         "Programming Language :: Python :: 3.11",
         "Programming Language :: Python :: 3.12",
+        "Programming Language :: Python :: 3.13",
         "Topic :: Software Development :: Libraries :: Python Modules",
     ],
-    keywords="AVM FRITZ!Box fritzbox fritz",
+    keywords="AVM FRITZ!Box fritzbox fritz TR-064 AHA-HTTP homeautomation",
     python_requires=">= 3.7",
     install_requires=["requests>=2.22.0",],
     extras_require={
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fritzconnection-1.13.2/tox.ini 
new/fritzconnection-1.14.0/tox.ini
--- old/fritzconnection-1.13.2/tox.ini  2023-09-17 14:58:30.000000000 +0200
+++ new/fritzconnection-1.14.0/tox.ini  1970-01-01 01:00:00.000000000 +0100
@@ -1,75 +0,0 @@
-[tox]
-skip_missing_interpreters = True
-envlist =
-    py37
-    py38
-    py39
-    py310
-    py311
-    py312
-    doc
-
-
-[testenv]
-passenv = LANG
-deps =
-    pytest
-;     segno
-;     opencv-python-headless
-
-commands = {envbindir}/pytest {posargs}
-
-
-[testenv:py38]
-deps =
-    pytest
-    segno
-    opencv-python-headless
-
-[testenv:py39]
-deps =
-    pytest
-    segno
-    opencv-python-headless
-
-[testenv:py310]
-deps =
-    pytest
-    segno
-    opencv-python-headless
-
-[testenv:py311]
-deps =
-    pytest
-    segno
-    opencv-python-headless
-
-[testenv:py312]
-deps =
-    pytest
-    segno
-;    opencv-python-headless  ; needs distutils removed from Python 3.12
-
-
-
-[testenv:doc]
-changedir = docs
-setenv = APIDOC=True
-
-deps =
-    sphinx
-    sphinx_rtd_theme
-
-commands =
-    sphinx-build -qb html -d {envtmpdir}/doctrees . {envtmpdir}/html
-
-
-[testenv:cov]
-passenv = COVERALLS_REPO_TOKEN GIT_*
-deps =
-    {[testenv]deps}
-    pytest-cov
-    coveralls
-commands =
-    pytest -qq --cov=fritzconnection --cov-report=term-missing
-#    coveralls

Reply via email to