python-netgear-switch-library 0.0.post323__tar.gz → 0.0.post325__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (377) hide show
  1. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/PKG-INFO +15 -1
  2. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/README.md +13 -0
  3. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/_ext/support_tables.py +258 -4
  4. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/conf.py +11 -0
  5. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/fake/testing.rst +55 -18
  6. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/guide/concepts.rst +65 -14
  7. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/guide/configuration.rst +49 -12
  8. python_netgear_switch_library-0.0.post325/docs/guide/quickstart.rst +311 -0
  9. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/guide/reading.rst +128 -39
  10. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/guide/writing.rst +58 -16
  11. python_netgear_switch_library-0.0.post325/docs/index.rst +170 -0
  12. python_netgear_switch_library-0.0.post325/docs/models/gs105pe.rst +43 -0
  13. python_netgear_switch_library-0.0.post325/docs/models/gs110emx.rst +52 -0
  14. python_netgear_switch_library-0.0.post325/docs/models/gs305ep.rst +63 -0
  15. python_netgear_switch_library-0.0.post325/docs/models/gs728tpp.rst +52 -0
  16. python_netgear_switch_library-0.0.post325/docs/models/gsm7228ps.rst +66 -0
  17. python_netgear_switch_library-0.0.post325/docs/models/gsm7252ps.rst +54 -0
  18. python_netgear_switch_library-0.0.post325/docs/models/index.rst +76 -0
  19. python_netgear_switch_library-0.0.post325/docs/models/m4300-16x.rst +60 -0
  20. python_netgear_switch_library-0.0.post325/docs/models/m4300-24x.rst +55 -0
  21. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/models/support.rst +8 -0
  22. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/protocols/cli.rst +76 -16
  23. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/protocols/http.rst +14 -0
  24. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/protocols/nsdp.rst +10 -0
  25. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/protocols/snmp.rst +10 -0
  26. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/pyproject.toml +1 -0
  27. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/__init__.py +10 -0
  28. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/_version.py +2 -2
  29. python_netgear_switch_library-0.0.post323/docs/guide/quickstart.rst +0 -173
  30. python_netgear_switch_library-0.0.post323/docs/index.rst +0 -112
  31. python_netgear_switch_library-0.0.post323/docs/models/index.rst +0 -175
  32. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/.github/workflows/ci.yml +0 -0
  33. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/.github/workflows/deb.yml +0 -0
  34. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/.github/workflows/publish-pypi.yml +0 -0
  35. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/.gitignore +0 -0
  36. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/.readthedocs.yaml +0 -0
  37. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/CLAUDE.md +0 -0
  38. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/LICENSE +0 -0
  39. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/RELEASING.md +0 -0
  40. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/debian/changelog +0 -0
  41. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/debian/control +0 -0
  42. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/debian/copyright +0 -0
  43. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/debian/rules +0 -0
  44. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/debian/source/format +0 -0
  45. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/Makefile +0 -0
  46. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/_ext/filelinks.py +0 -0
  47. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/_static/ngsw.css +0 -0
  48. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/cli.rst +0 -0
  49. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/core.rst +0 -0
  50. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/facades.rst +0 -0
  51. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/index.rst +0 -0
  52. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/mcp.rst +0 -0
  53. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/operations.rst +0 -0
  54. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/protocols.rst +0 -0
  55. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/transport.rst +0 -0
  56. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/api/virtual.rst +0 -0
  57. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/cli.rst +0 -0
  58. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/development.rst +0 -0
  59. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/fake/index.rst +0 -0
  60. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/fake/internals.rst +0 -0
  61. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/fake/serving.rst +0 -0
  62. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/guide/installation.rst +0 -0
  63. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/guide/principles.rst +0 -0
  64. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/mcp.rst +0 -0
  65. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/protocols/index.rst +0 -0
  66. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
  67. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
  68. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
  69. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
  70. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
  71. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
  72. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
  73. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
  74. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
  75. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
  76. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
  77. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/packaging/apt-index.html +0 -0
  78. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/packaging/ci-build.sh +0 -0
  79. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/packaging/deb-version.py +0 -0
  80. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/_dispatch.py +0 -0
  81. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/aio_api.py +0 -0
  82. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/capabilities.py +0 -0
  83. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/__init__.py +0 -0
  84. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/capture.py +0 -0
  85. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/context.py +0 -0
  86. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/format.py +0 -0
  87. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/main.py +0 -0
  88. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/resolve.py +0 -0
  89. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli/safety.py +0 -0
  90. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli_read.py +0 -0
  91. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/cli_write.py +0 -0
  92. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/config.py +0 -0
  93. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/errors.py +0 -0
  94. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/http_read.py +0 -0
  95. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/http_write.py +0 -0
  96. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/mcp/__init__.py +0 -0
  97. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/mcp/server.py +0 -0
  98. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/models.py +0 -0
  99. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/nsdp_read.py +0 -0
  100. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/nsdp_write.py +0 -0
  101. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/__init__.py +0 -0
  102. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/cli/__init__.py +0 -0
  103. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/cli/commands.py +0 -0
  104. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/cli/parse.py +0 -0
  105. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/__init__.py +0 -0
  106. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/crypt.py +0 -0
  107. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/endpoints.py +0 -0
  108. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/forms.py +0 -0
  109. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/parse.py +0 -0
  110. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/session.py +0 -0
  111. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/http/types.py +0 -0
  112. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
  113. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
  114. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/client.py +0 -0
  115. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
  116. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
  117. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/types.py +0 -0
  118. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/nsdp/write.py +0 -0
  119. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
  120. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/snmp/client.py +0 -0
  121. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/snmp/oids.py +0 -0
  122. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/snmp/parse.py +0 -0
  123. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/protocols/snmp/write.py +0 -0
  124. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/py.typed +0 -0
  125. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/registry.py +0 -0
  126. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/snmp_read.py +0 -0
  127. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/snmp_write.py +0 -0
  128. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/sync_api.py +0 -0
  129. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/__init__.py +0 -0
  130. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/aio/__init__.py +0 -0
  131. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
  132. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
  133. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/cli/__init__.py +0 -0
  134. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/cli/console.py +0 -0
  135. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/cli/session.py +0 -0
  136. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/cli/ssh.py +0 -0
  137. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/cli/telnet.py +0 -0
  138. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/http/__init__.py +0 -0
  139. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/http/client.py +0 -0
  140. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/sync/__init__.py +0 -0
  141. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
  142. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
  143. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/__init__.py +0 -0
  144. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/cli_fastpath.py +0 -0
  145. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/faces/__init__.py +0 -0
  146. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/faces/cli.py +0 -0
  147. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/faces/http.py +0 -0
  148. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/faces/mibview.py +0 -0
  149. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
  150. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/faces/snmp.py +0 -0
  151. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/seed.py +0 -0
  152. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/server.py +0 -0
  153. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/state.py +0 -0
  154. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web.py +0 -0
  155. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
  156. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
  157. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
  158. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
  159. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
  160. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
  161. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
  162. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
  163. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/src/netgear_switch/virtual/web_m4300.py +0 -0
  164. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/capture_parity.py +0 -0
  165. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_capture.py +0 -0
  166. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_cli_integration.py +0 -0
  167. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_control_poe_port.py +0 -0
  168. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_control_vlan_ip.py +0 -0
  169. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_format.py +0 -0
  170. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_main_framework.py +0 -0
  171. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_new_commands.py +0 -0
  172. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_op_coverage.py +0 -0
  173. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_prompt_wiring.py +0 -0
  174. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_read_commands.py +0 -0
  175. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_resolve.py +0 -0
  176. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_safety.py +0 -0
  177. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/cli/test_serve.py +0 -0
  178. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/conftest.py +0 -0
  179. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/equivalence.py +0 -0
  180. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/captures/gsm7228ps.json +0 -0
  181. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/captures/gsm7252ps.json +0 -0
  182. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/captures/m4300-16x.json +0 -0
  183. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/captures/m4300-24x.json +0 -0
  184. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
  185. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
  186. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
  187. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
  188. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
  189. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
  190. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
  191. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
  192. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
  193. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
  194. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
  195. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
  196. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
  197. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
  198. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
  199. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
  200. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
  201. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
  202. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
  203. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
  204. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
  205. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
  206. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
  207. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
  208. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
  209. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
  210. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
  211. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
  212. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
  213. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
  214. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
  215. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
  216. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
  217. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
  218. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
  219. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
  220. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
  221. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
  222. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
  223. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
  224. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
  225. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
  226. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
  227. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
  228. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
  229. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs105pe_membership.html +0 -0
  230. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs105pe_portstats.html +0 -0
  231. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs105pe_pvid.html +0 -0
  232. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs105pe_status.html +0 -0
  233. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
  234. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
  235. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
  236. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
  237. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_login.html +0 -0
  238. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
  239. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_pvid.html +0 -0
  240. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_redirect.html +0 -0
  241. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
  242. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
  243. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
  244. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_login.html +0 -0
  245. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_membership.html +0 -0
  246. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
  247. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_portstats.html +0 -0
  248. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_pvid.html +0 -0
  249. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
  250. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
  251. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
  252. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
  253. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
  254. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
  255. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
  256. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
  257. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
  258. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
  259. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
  260. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
  261. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
  262. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
  263. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
  264. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
  265. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
  266. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
  267. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
  268. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
  269. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
  270. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
  271. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
  272. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_login.html +0 -0
  273. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
  274. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
  275. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
  276. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
  277. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
  278. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
  279. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
  280. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
  281. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
  282. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
  283. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
  284. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
  285. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
  286. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
  287. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_addresstable.html +0 -0
  288. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
  289. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
  290. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_ports.html +0 -0
  291. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_portstats.html +0 -0
  292. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_pvid.html +0 -0
  293. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_sysinfo.html +0 -0
  294. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
  295. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
  296. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
  297. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
  298. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
  299. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
  300. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/http_specs.py +0 -0
  301. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/cli/test_cli_parse.py +0 -0
  302. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/http/test_crypt.py +0 -0
  303. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/http/test_endpoints.py +0 -0
  304. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/http/test_forms.py +0 -0
  305. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/http/test_parse.py +0 -0
  306. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/nsdp/test_auth.py +0 -0
  307. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/nsdp/test_parsers.py +0 -0
  308. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/nsdp/test_protocol.py +0 -0
  309. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/nsdp/test_write_frame.py +0 -0
  310. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_client.py +0 -0
  311. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_oids.py +0 -0
  312. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
  313. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
  314. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
  315. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_parse_ports.py +0 -0
  316. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_parse_system_info.py +0 -0
  317. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_parse_vlans.py +0 -0
  318. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/protocols/snmp/test_write_encode.py +0 -0
  319. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_aio_api.py +0 -0
  320. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_capabilities.py +0 -0
  321. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_cli_read.py +0 -0
  322. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_cli_vlan_write.py +0 -0
  323. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_cli_write.py +0 -0
  324. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_config.py +0 -0
  325. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_cross_backend_equivalence.py +0 -0
  326. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_deb_version.py +0 -0
  327. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_dispatch.py +0 -0
  328. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_errors.py +0 -0
  329. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_facade_equivalence.py +0 -0
  330. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_http_dispatch.py +0 -0
  331. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_http_equivalence.py +0 -0
  332. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_http_read.py +0 -0
  333. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_http_vlan_membership.py +0 -0
  334. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_http_write.py +0 -0
  335. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_http_xui_writes.py +0 -0
  336. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_import.py +0 -0
  337. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_mcp_server.py +0 -0
  338. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_models.py +0 -0
  339. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_models_snmp_read.py +0 -0
  340. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_nsdp_integration.py +0 -0
  341. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_nsdp_read.py +0 -0
  342. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_nsdp_write.py +0 -0
  343. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_public_api.py +0 -0
  344. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_quality_gates.py +0 -0
  345. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_registry.py +0 -0
  346. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_snmp_integration.py +0 -0
  347. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_snmp_read.py +0 -0
  348. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_snmp_write.py +0 -0
  349. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_sync_api.py +0 -0
  350. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_version.py +0 -0
  351. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/test_write_equivalence.py +0 -0
  352. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/_fastpath_fake.py +0 -0
  353. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_cli_console.py +0 -0
  354. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_cli_scp_cert.py +0 -0
  355. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_cli_telnet.py +0 -0
  356. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_http_client.py +0 -0
  357. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_nsdp_udp_sync.py +0 -0
  358. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_set_transport.py +0 -0
  359. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
  360. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_snmp_pysnmp.py +0 -0
  361. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/transport/test_value_parity.py +0 -0
  362. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_cli_cross_backend.py +0 -0
  363. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
  364. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_gsm7228ps_seed.py +0 -0
  365. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_m4300_seeds.py +0 -0
  366. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_mibview.py +0 -0
  367. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_mutable_state.py +0 -0
  368. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_nsdp_state.py +0 -0
  369. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_serve_daemon.py +0 -0
  370. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_snmp_write_face.py +0 -0
  371. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_state_seed.py +0 -0
  372. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_switchport_vlan_write.py +0 -0
  373. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_virtual_http_face.py +0 -0
  374. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_virtual_nsdp_face.py +0 -0
  375. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_virtual_snmp_face.py +0 -0
  376. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/tests/virtual/test_web_projection.py +0 -0
  377. {python_netgear_switch_library-0.0.post323 → python_netgear_switch_library-0.0.post325}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-netgear-switch-library
3
- Version: 0.0.post323
3
+ Version: 0.0.post325
4
4
  Summary: Python library and CLI to query and control Netgear switches over SNMP, NSDP and HTTP.
5
5
  Author-email: Tim Ansell <me@mith.ro>
6
6
  License-Expression: Apache-2.0
@@ -18,6 +18,7 @@ Requires-Dist: pyserial>=3.5; extra == 'docs'
18
18
  Requires-Dist: pysnmp>=7.0; extra == 'docs'
19
19
  Requires-Dist: sphinx-argparse>=0.5; extra == 'docs'
20
20
  Requires-Dist: sphinx-copybutton>=0.5.2; extra == 'docs'
21
+ Requires-Dist: sphinx-design>=0.6; extra == 'docs'
21
22
  Requires-Dist: sphinx>=8.1; extra == 'docs'
22
23
  Provides-Extra: http
23
24
  Requires-Dist: httpx>=0.27; extra == 'http'
@@ -37,6 +38,19 @@ Description-Content-Type: text/markdown
37
38
  Query and control all your Netgear switches — SNMP (managed), NSDP and HTTP
38
39
  web-UI (Plus) — behind one model-driven Python API and the `ngsw` CLI.
39
40
 
41
+ > [!WARNING]
42
+ > **This library and its documentation were written by an AI.** Every line of
43
+ > code, every test and every documentation page was generated by a language
44
+ > model, with a human directing and reviewing the work.
45
+ >
46
+ > The device behaviour in it is *measured* — protocol details and firmware
47
+ > quirks come from captured traffic and live runs against the real switches,
48
+ > recorded with the host and firmware they were seen on — and the support tables
49
+ > are generated from the code rather than written by hand. But an AI is
50
+ > perfectly capable of confident, fluent error, and nothing here has had
51
+ > independent human review at scale. Verify anything that matters to you against
52
+ > the source or your own hardware, and please report what you find.
53
+
40
54
  **Documentation: <https://python-netgear-switch-library.readthedocs.io/>** — API
41
55
  reference, the complete `ngsw` CLI reference, the virtual-switch guide (how to
42
56
  test your own tools against a mock), and generated
@@ -3,6 +3,19 @@
3
3
  Query and control all your Netgear switches — SNMP (managed), NSDP and HTTP
4
4
  web-UI (Plus) — behind one model-driven Python API and the `ngsw` CLI.
5
5
 
6
+ > [!WARNING]
7
+ > **This library and its documentation were written by an AI.** Every line of
8
+ > code, every test and every documentation page was generated by a language
9
+ > model, with a human directing and reviewing the work.
10
+ >
11
+ > The device behaviour in it is *measured* — protocol details and firmware
12
+ > quirks come from captured traffic and live runs against the real switches,
13
+ > recorded with the host and firmware they were seen on — and the support tables
14
+ > are generated from the code rather than written by hand. But an AI is
15
+ > perfectly capable of confident, fluent error, and nothing here has had
16
+ > independent human review at scale. Verify anything that matters to you against
17
+ > the source or your own hardware, and please report what you find.
18
+
6
19
  **Documentation: <https://python-netgear-switch-library.readthedocs.io/>** — API
7
20
  reference, the complete `ngsw` CLI reference, the virtual-switch guide (how to
8
21
  test your own tools against a mock), and generated
@@ -29,9 +29,11 @@ Directives
29
29
 
30
30
  from __future__ import annotations
31
31
 
32
+ from pathlib import Path
32
33
  from typing import TYPE_CHECKING, Any, ClassVar
33
34
 
34
35
  from docutils.parsers.rst import directives
36
+ from sphinx.util import logging
35
37
  from sphinx.util.docutils import SphinxDirective
36
38
 
37
39
  from netgear_switch.capabilities import (
@@ -49,6 +51,8 @@ if TYPE_CHECKING:
49
51
  from docutils import nodes
50
52
  from sphinx.application import Sphinx
51
53
 
54
+ logger = logging.getLogger(__name__)
55
+
52
56
  #: Compact per-backend labels used inside grid cells. The CLI backends share a
53
57
  #: label because they are one command surface reached over three transports
54
58
  #: (see ``src/netgear_switch/registry.py``'s ``Backend``).
@@ -65,6 +69,24 @@ _YES = "✓" # check mark
65
69
  _NO = "—" # em dash
66
70
 
67
71
 
72
+ def matrix_models() -> tuple[str, ...]:
73
+ """The models the generated tables cover.
74
+
75
+ A model registered from a specification sheet with **no device access**
76
+ (``SwitchModel.verified is False``) is deliberately excluded. Listing one in
77
+ a support matrix would assert per-model, per-backend behaviour that nobody
78
+ has ever observed on that hardware -- the exact difference between a
79
+ measurement and an inference this project refuses to blur. They are named in
80
+ prose instead, so their existence is not hidden either.
81
+ """
82
+ return tuple(key for key, model in MODELS.items() if model.verified)
83
+
84
+
85
+ def unverified_models() -> tuple[str, ...]:
86
+ """Registered models with no capture behind them, excluded from the tables."""
87
+ return tuple(key for key, model in MODELS.items() if not model.verified)
88
+
89
+
68
90
  def _rst_escape(text: str) -> str:
69
91
  """Neutralise inline reST markup in generated cell text."""
70
92
  for char in ("\\", "*", "`", "|", "_"):
@@ -114,7 +136,8 @@ class ModelTable(_GeneratedTable):
114
136
  for alias, canonical in MODEL_ALIASES.items():
115
137
  aliases.setdefault(canonical, []).append(alias)
116
138
  rows = []
117
- for key, model in MODELS.items():
139
+ for key in matrix_models():
140
+ model = MODELS[key]
118
141
  also = aliases.get(key)
119
142
  name = f"``{key}``"
120
143
  if also:
@@ -159,7 +182,8 @@ class ProtocolTable(_GeneratedTable):
159
182
 
160
183
  def body(self) -> str:
161
184
  rows = []
162
- for key, model in MODELS.items():
185
+ for key in matrix_models():
186
+ model = MODELS[key]
163
187
  cells = [_YES if b in model.backends else _NO for b in self._COLUMNS]
164
188
  rows.append([f"``{key}``", *cells])
165
189
  return _list_table(
@@ -185,7 +209,7 @@ class OperationTable(_GeneratedTable):
185
209
  raise self.error(f"ngsw-operation-table: unknown kind {kind!r}")
186
210
 
187
211
  def body(self) -> str:
188
- keys = list(MODELS)
212
+ keys = list(matrix_models())
189
213
  rows = []
190
214
  for op in self._operations():
191
215
  cells = []
@@ -251,7 +275,7 @@ class SupportGaps(_GeneratedTable):
251
275
 
252
276
  def body(self) -> str:
253
277
  rows = []
254
- for key in MODELS:
278
+ for key in matrix_models():
255
279
  backends = backends_for(key)
256
280
  if len(backends) < 2:
257
281
  continue
@@ -282,10 +306,240 @@ class SupportGaps(_GeneratedTable):
282
306
  )
283
307
 
284
308
 
309
+ _BACKEND_BY_NAME = {b.name: b for b in Backend}
310
+
311
+ #: CLI is one command surface over three transports, so a "CLI" column means
312
+ #: whichever of SSH/TELNET/CONSOLE a model actually registers.
313
+ _CLI = (Backend.SSH, Backend.TELNET, Backend.CONSOLE)
314
+
315
+
316
+ def _named_backends(name: str) -> tuple[Backend, ...]:
317
+ key = name.strip().upper()
318
+ if key == "CLI":
319
+ return _CLI
320
+ try:
321
+ return (_BACKEND_BY_NAME[key],)
322
+ except KeyError:
323
+ raise ValueError(f"unknown backend: {name!r}") from None
324
+
325
+
326
+ def _models_with(backends: tuple[Backend, ...]) -> tuple[str, ...]:
327
+ return tuple(
328
+ key
329
+ for key in matrix_models()
330
+ if any(b in MODELS[key].backends for b in backends)
331
+ )
332
+
333
+
334
+ class BackendModelTable(_GeneratedTable):
335
+ """Which models expose one backend, and how they are reached."""
336
+
337
+ required_arguments = 1
338
+
339
+ def body(self) -> str:
340
+ backends = _named_backends(self.arguments[0])
341
+ rows = []
342
+ for key in _models_with(backends):
343
+ model = MODELS[key]
344
+ reached = ", ".join(
345
+ b.name for b in backends_for(model) if b in backends
346
+ )
347
+ rows.append(
348
+ [
349
+ # Absolute doc path: this table is included from
350
+ # docs/protocols/, so a relative target would resolve there.
351
+ f":doc:`{key} </models/{key}>`",
352
+ _rst_escape(model.display_name),
353
+ model.switch_class.value.replace("_", " "),
354
+ str(model.port_count),
355
+ reached,
356
+ ]
357
+ )
358
+ if not rows:
359
+ return "No registered model exposes this backend.\n"
360
+ return _list_table(
361
+ "",
362
+ ["Model", "Product", "Class", "Ports", "Reached over"],
363
+ rows,
364
+ widths="16 24 18 10 32",
365
+ )
366
+
367
+
368
+ class BackendOperationTable(_GeneratedTable):
369
+ """Operation x model grid for ONE backend: what it can do, where."""
370
+
371
+ required_arguments = 1
372
+
373
+ def body(self) -> str:
374
+ backends = _named_backends(self.arguments[0])
375
+ keys = _models_with(backends)
376
+ if not keys:
377
+ return "No registered model exposes this backend.\n"
378
+ rows = []
379
+ notes: list[str] = []
380
+ seen: dict[str, int] = {}
381
+ label = self.arguments[0].strip().upper()
382
+ for op in (*READ_OPERATIONS, *WRITE_OPERATIONS):
383
+ cells = []
384
+ for key in keys:
385
+ backend = next(
386
+ (b for b in backends_for(key) if b in backends), None
387
+ )
388
+ if backend is None: # pragma: no cover - filtered by _models_with
389
+ cells.append(_NO)
390
+ continue
391
+ cap = support(key, backend, op)
392
+ if cap.supported:
393
+ cells.append(_YES)
394
+ continue
395
+ reason = _rst_escape(cap.reason)
396
+ index = seen.get(reason)
397
+ if index is None:
398
+ index = len(notes) + 1
399
+ seen[reason] = index
400
+ notes.append(reason)
401
+ cells.append(f"{_NO} [{label}-{index}]_")
402
+ rows.append([f":py:meth:`~netgear_switch.SyncSwitch.{op.name}`", *cells])
403
+ table = _list_table("", ["Operation", *keys], rows)
404
+ if not notes:
405
+ return table
406
+ citations = "\n".join(
407
+ f".. [{label}-{i}] {text}" for i, text in enumerate(notes, 1)
408
+ )
409
+ return f"{table}\n{citations}\n"
410
+
411
+
412
+ class ModelFacts(_GeneratedTable):
413
+ """The registry record for one model, as a field list."""
414
+
415
+ required_arguments = 1
416
+
417
+ def body(self) -> str:
418
+ model = get_model(self.arguments[0])
419
+ aliases = sorted(k for k, v in MODEL_ALIASES.items() if v == model.key)
420
+ vendor = model.snmp_vendor_base or "none (standard MIBs only)"
421
+ dialect = {
422
+ "qbridge": "Q-BRIDGE static PortLists (read-modify-write)",
423
+ "fastpath_switchport": "FASTPATH vendor switchport table",
424
+ }.get(model.snmp_vlan_write, model.snmp_vlan_write)
425
+ rows = [
426
+ ["Registry key", f"``{model.key}``"],
427
+ ["Product name", _rst_escape(model.display_name)],
428
+ ["Class", model.switch_class.value.replace("_", " ")],
429
+ ["Ports", str(model.port_count)],
430
+ ["PoE (PSE) ports", str(model.poe_port_count)],
431
+ [
432
+ "Backends",
433
+ ", ".join(f"``{b.name}``" for b in backends_for(model)),
434
+ ],
435
+ ["SNMP vendor subtree", f"``{vendor}``"],
436
+ ]
437
+ if Backend.SNMP in model.backends:
438
+ rows.append(["VLAN write dialect", dialect])
439
+ if model.snmp_vlan_split_membership_writes:
440
+ rows.append(
441
+ [
442
+ "VLAN write quirk",
443
+ "egress and untagged PortLists must travel in "
444
+ "SEPARATE PDUs, egress first",
445
+ ]
446
+ )
447
+ if aliases:
448
+ rows.append(
449
+ ["Aliases", ", ".join(f"``{a}``" for a in aliases)]
450
+ )
451
+ rows.append(
452
+ [
453
+ "MAC/FDB table",
454
+ "yes" if model.has_mac_table else "no",
455
+ ]
456
+ )
457
+ return _list_table("", ["Field", "Value"], rows, widths="30 70")
458
+
459
+
460
+ class ModelPhoto(_GeneratedTable):
461
+ """Render a photo of the switch, if one has been added to the repository.
462
+
463
+ Photos live at ``docs/_static/models/<key>.<ext>``. Nothing is emitted when
464
+ the file is absent -- a missing photo must not render as a broken image --
465
+ and every gap is reported once at the end of the build.
466
+ """
467
+
468
+ required_arguments = 1
469
+ option_spec: ClassVar[dict[str, Any]] = {"alt": directives.unchanged}
470
+
471
+ _EXTENSIONS = (".jpg", ".jpeg", ".png", ".webp")
472
+
473
+ def body(self) -> str:
474
+ key = self.arguments[0]
475
+ model = get_model(key)
476
+ static = Path(str(self.env.srcdir)) / "_static" / "models"
477
+ for ext in self._EXTENSIONS:
478
+ if (static / f"{key}{ext}").is_file():
479
+ alt = self.options.get("alt", f"{model.display_name} switch")
480
+ return (
481
+ f".. figure:: /_static/models/{key}{ext}\n"
482
+ f" :alt: {alt}\n"
483
+ " :align: center\n"
484
+ " :class: ngsw-model-photo\n\n"
485
+ f" {_rst_escape(model.display_name)}\n"
486
+ )
487
+ MISSING_PHOTOS.add(key)
488
+ return ""
489
+
490
+
491
+ #: Models whose photo slot was empty during this build; reported at the end.
492
+ MISSING_PHOTOS: set[str] = set()
493
+
494
+
495
+ class UnverifiedNote(_GeneratedTable):
496
+ """Name the models the tables leave out, and say why."""
497
+
498
+ def body(self) -> str:
499
+ keys = unverified_models()
500
+ if not keys:
501
+ return ""
502
+ listed = ", ".join(f"``{k}`` ({MODELS[k].display_name})" for k in keys)
503
+ return (
504
+ ".. note::\n\n"
505
+ f" The registry also carries {listed}, which the tables above "
506
+ "deliberately leave out.\n"
507
+ " No device of either kind has ever been reachable from this "
508
+ "project, so there is no\n"
509
+ " capture, no live run, and nothing measured to report. They are "
510
+ "registered only so a\n"
511
+ " caller can construct a facade for them; treat every one of "
512
+ "their fields as a\n"
513
+ " specification-sheet guess until a real capture exists.\n"
514
+ )
515
+
516
+
517
+ def _report_missing_photos(app: Sphinx, exception: Exception | None) -> None:
518
+ """Say which model pages have no photo yet, rather than failing the build.
519
+
520
+ A photo is content the repository may simply not have; the build must not
521
+ break over it, but the gap should be visible instead of silently invisible.
522
+ """
523
+ if exception is not None or not MISSING_PHOTOS:
524
+ return
525
+ logger.info(
526
+ "ngsw: no photo for %d model(s): %s "
527
+ "(add docs/_static/models/<key>.jpg to fill the slot)",
528
+ len(MISSING_PHOTOS),
529
+ ", ".join(sorted(MISSING_PHOTOS)),
530
+ )
531
+
532
+
285
533
  def setup(app: Sphinx) -> dict[str, Any]:
534
+ app.add_directive("ngsw-unverified-note", UnverifiedNote)
286
535
  app.add_directive("ngsw-model-table", ModelTable)
287
536
  app.add_directive("ngsw-protocol-table", ProtocolTable)
288
537
  app.add_directive("ngsw-operation-table", OperationTable)
289
538
  app.add_directive("ngsw-model-support", ModelSupportTable)
539
+ app.add_directive("ngsw-model-facts", ModelFacts)
540
+ app.add_directive("ngsw-model-photo", ModelPhoto)
541
+ app.add_directive("ngsw-backend-models", BackendModelTable)
542
+ app.add_directive("ngsw-backend-operations", BackendOperationTable)
290
543
  app.add_directive("ngsw-support-gaps", SupportGaps)
544
+ app.connect("build-finished", _report_missing_photos)
291
545
  return {"version": "1.0", "parallel_read_safe": True, "parallel_write_safe": True}
@@ -54,6 +54,9 @@ extensions = [
54
54
  "sphinx.ext.intersphinx",
55
55
  "sphinx.ext.extlinks",
56
56
  "sphinx_copybutton",
57
+ # Synchronised tab sets: every API example is shown in both synchronous and
58
+ # asynchronous form, and picking one switches every example on the page.
59
+ "sphinx_design",
57
60
  "sphinxarg.ext",
58
61
  # Local: see docs/_ext/.
59
62
  "filelinks",
@@ -135,6 +138,14 @@ html_theme_options = {
135
138
  "source_repository": f"{GITHUB_REPO}/",
136
139
  "source_branch": filelinks_ref,
137
140
  "source_directory": "docs/",
141
+ # Shown at the top of EVERY page. This library and this documentation were
142
+ # written by an AI; a reader deserves to know that before they trust a
143
+ # sentence of it, not only if they happen to land on the front page.
144
+ "announcement": (
145
+ "This library and its documentation were written by an AI. "
146
+ '<a href="/en/latest/#ai-generated">What that means, and what was '
147
+ "actually verified against hardware</a>."
148
+ ),
138
149
  }
139
150
 
140
151
  copybutton_prompt_text = r"\$ |>>> |\.\.\. "
@@ -54,21 +54,53 @@ Testing code that uses this library
54
54
  Inject a client pointed at the mock. Everything above the transport — dispatch,
55
55
  parsing, model rules — is the same code that runs against hardware.
56
56
 
57
- .. code-block:: python
57
+ .. tab-set::
58
58
 
59
- from netgear_switch import SyncSwitch, get_model
60
- from netgear_switch.transport.sync.snmp_netsnmp_cli import NetsnmpCliClient
61
- from netgear_switch.virtual.server import VirtualSwitch
59
+ .. tab-item:: Sync
60
+ :sync: sync
61
+
62
+ .. code-block:: python
63
+
64
+ from netgear_switch import SyncSwitch, get_model
65
+ from netgear_switch.transport.sync.snmp_netsnmp_cli import NetsnmpCliClient
66
+ from netgear_switch.virtual.server import VirtualSwitch
67
+
68
+
69
+ def test_reports_link_state() -> None:
70
+ with VirtualSwitch(model="gsm7252ps") as mock:
71
+ switch = SyncSwitch(
72
+ get_model("gsm7252ps"),
73
+ host=mock.host,
74
+ snmp_client=NetsnmpCliClient(
75
+ f"{mock.host}:{mock.port}", "public"
76
+ ),
77
+ )
78
+ assert switch.get_ports()[0].link_up
79
+
80
+ .. tab-item:: Async
81
+ :sync: async
82
+
83
+ .. code-block:: python
84
+
85
+ import pytest
86
+
87
+ from netgear_switch import AsyncSwitch, get_model
88
+ from netgear_switch.transport.aio.snmp_pysnmp import PysnmpClient
89
+ from netgear_switch.virtual.server import VirtualSwitch
62
90
 
63
91
 
64
- def test_reports_link_state() -> None:
65
- with VirtualSwitch(model="gsm7252ps") as mock:
66
- switch = SyncSwitch(
67
- get_model("gsm7252ps"),
68
- host=mock.host,
69
- snmp_client=NetsnmpCliClient(f"{mock.host}:{mock.port}", "public"),
70
- )
71
- assert switch.get_ports()[0].link_up
92
+ @pytest.mark.asyncio
93
+ async def test_reports_link_state() -> None:
94
+ with VirtualSwitch(model="gsm7252ps") as mock:
95
+ switch = AsyncSwitch(
96
+ get_model("gsm7252ps"),
97
+ host=mock.host,
98
+ snmp_client=PysnmpClient(mock.host, "public", port=mock.port),
99
+ )
100
+ try:
101
+ assert (await switch.get_ports())[0].link_up
102
+ finally:
103
+ await switch.aclose()
72
104
 
73
105
  .. code-block:: text
74
106
 
@@ -84,14 +116,19 @@ Every backend takes an injected client:
84
116
  * - Backend
85
117
  - Constructor argument
86
118
  * - SNMP
87
- - ``snmp_client=NetsnmpCliClient(f"{host}:{port}", community)``, and
88
- ``snmp_write_client=`` for writes
119
+ - sync: ``snmp_client=NetsnmpCliClient(f"{host}:{port}", community)``;
120
+ async: ``snmp_client=PysnmpClient(host, community, port=mock.port)``.
121
+ Add ``snmp_write_client=`` for writes.
89
122
  * - NSDP
90
- - ``nsdp_client=UdpNsdpClient(host, client_port=0, server_port=mock.port)``
123
+ - sync: ``nsdp_client=UdpNsdpClient(host, client_port=0, server_port=mock.port)``;
124
+ async: the same arguments to ``AsyncUdpNsdpClient``.
91
125
  * - HTTP
92
- - ``http_client=HttpClient(f"{host}:{http_port}", password, http_spec(model))``
126
+ - sync: ``http_client=HttpClient(f"{host}:{http_port}", password, http_spec(model))``;
127
+ async: the same arguments to ``AsyncHttpClient``.
93
128
  * - CLI
94
- - ``cli_client=mock.cli_session()``
129
+ - ``cli_client=mock.cli_session()`` — **synchronous only**. The CLI
130
+ transports block, so :py:class:`~netgear_switch.AsyncSwitch` has no CLI
131
+ backend and takes no ``cli_client``.
95
132
 
96
133
  All four at once
97
134
  ----------------
@@ -274,7 +311,7 @@ with the wrong model:
274
311
  host=mock.host,
275
312
  snmp_client=NetsnmpCliClient(f"{mock.host}:{mock.port}", "public"),
276
313
  )
277
- print(switch.identify())
314
+ print(switch.identify()) # await switch.identify() on AsyncSwitch
278
315
 
279
316
  .. code-block:: text
280
317
 
@@ -64,10 +64,23 @@ gaps that remain.
64
64
  Dispatch: exactly one backend, every time
65
65
  -----------------------------------------
66
66
 
67
- .. code-block:: python
67
+ .. tab-set::
68
68
 
69
- switch.get_vlans() # the model's default backend
70
- switch.get_vlans(backend=Backend.HTTP) # exactly HTTP, or an error
69
+ .. tab-item:: Sync
70
+ :sync: sync
71
+
72
+ .. code-block:: python
73
+
74
+ switch.get_vlans() # the model's default backend
75
+ switch.get_vlans(backend=Backend.HTTP) # exactly HTTP, or an error
76
+
77
+ .. tab-item:: Async
78
+ :sync: async
79
+
80
+ .. code-block:: python
81
+
82
+ await switch.get_vlans() # the model's default backend
83
+ await switch.get_vlans(backend=Backend.HTTP) # exactly HTTP, or an error
71
84
 
72
85
  When you **name** a backend, that backend runs. If the model does not have it,
73
86
  you get `UnsupportedCapabilityError` immediately. If it has it but cannot serve
@@ -106,12 +119,27 @@ Choosing a default per facade
106
119
 
107
120
  Pass ``backend=`` to the constructor to change the default for every call:
108
121
 
109
- .. code-block:: python
122
+ .. tab-set::
123
+
124
+ .. tab-item:: Sync
125
+ :sync: sync
126
+
127
+ .. code-block:: python
128
+
129
+ web_only = SyncSwitch(
130
+ get_model("gsm7252ps"), host="10.1.5.22",
131
+ http_password="...", backend=Backend.HTTP,
132
+ )
110
133
 
111
- web_only = SyncSwitch(
112
- get_model("gsm7252ps"), host="10.1.5.22",
113
- http_password="...", backend=Backend.HTTP,
114
- )
134
+ .. tab-item:: Async
135
+ :sync: async
136
+
137
+ .. code-block:: python
138
+
139
+ web_only = AsyncSwitch(
140
+ get_model("gsm7252ps"), host="10.1.5.22",
141
+ http_password="...", backend=Backend.HTTP,
142
+ )
115
143
 
116
144
  Per-call ``backend=`` still overrides it.
117
145
 
@@ -121,11 +149,13 @@ Verification status
121
149
  This project distinguishes what has been *measured* from what has been
122
150
  *assumed*, and the distinction is visible in the API.
123
151
 
124
- ``SwitchModel.verified``
125
- ``False`` marks a model registered from a specification sheet with no device
126
- capture behind it currently ``m7300`` and ``xs748t``. Standard-MIB reads
127
- should work; vendor-specific reads are unverified. These models are labelled
128
- in :doc:`../models/index` and never treated as evidence of anything.
152
+ `SwitchModel.verified`
153
+ ``False`` marks a model registered from a specification sheet, with no
154
+ device of that kind ever reachable from this project. Such a model is
155
+ **excluded from every support table** see :doc:`../models/index`
156
+ because putting it in a matrix would assert per-backend behaviour nobody has
157
+ observed. It stays in the registry only so a caller can construct a facade
158
+ for it, and nothing here treats it as evidence of anything.
129
159
 
130
160
  ``HttpModelSpec.reads_verified``, ``CliModelSpec.reads_verified`` / ``writes_verified``
131
161
  Per-model, per-backend flags recording whether that backend's output has
@@ -173,8 +203,29 @@ see :doc:`../models/support`.
173
203
  Synchronous and asynchronous
174
204
  ----------------------------
175
205
 
176
- `SyncSwitch` and `AsyncSwitch` expose the same operations with the same
206
+ :py:class:`~netgear_switch.SyncSwitch` and
207
+ :py:class:`~netgear_switch.AsyncSwitch` expose the same operations with the same
177
208
  arguments and the same semantics. They share the model registry, the parsers and
178
209
  the backend-resolution seam in ``src/netgear_switch/_dispatch.py``; only the
179
210
  transports differ. ``tests/test_facade_equivalence.py`` asserts they stay in
180
211
  step.
212
+
213
+ .. important::
214
+
215
+ **One backend is synchronous-only: the CLI.** All three CLI transports —
216
+ SSH, telnet and the serial console — are blocking, and none has an async
217
+ twin, so :py:class:`~netgear_switch.AsyncSwitch` has no CLI backend at all.
218
+ Asking for one raises rather than quietly blocking the event loop::
219
+
220
+ UnsupportedCapabilityError: model 'gsm7252ps' CLI reads are not available
221
+ via the async facade (CLI is synchronous ...)
222
+
223
+ That also rules out
224
+ :py:meth:`~netgear_switch.SyncSwitch.upload_certificate_scp` on the async
225
+ facade, since the operation is CLI-based by nature. Use
226
+ :py:class:`~netgear_switch.SyncSwitch` for those, or wrap the call in
227
+ :py:func:`asyncio.to_thread`.
228
+
229
+ **The support tables in** :doc:`../models/support` **describe the
230
+ synchronous facade.** Every SNMP, NSDP and HTTP entry holds for both; the CLI
231
+ columns apply to :py:class:`~netgear_switch.SyncSwitch` only.