python-netgear-switch-library 0.0.post326__tar.gz → 0.0.post329__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 (381) hide show
  1. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/PKG-INFO +1 -1
  2. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/_ext/support_tables.py +379 -24
  3. python_netgear_switch_library-0.0.post329/docs/_static/ngsw.css +44 -0
  4. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/gs105pe.rst +2 -0
  5. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/gs110emx.rst +2 -0
  6. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/gs305ep.rst +2 -0
  7. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/gs728tpp.rst +2 -0
  8. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/gsm7228ps.rst +2 -0
  9. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/gsm7252ps.rst +2 -0
  10. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/m4300-16x.rst +2 -0
  11. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/m4300-24x.rst +2 -0
  12. python_netgear_switch_library-0.0.post329/docs/protocols/http/cheetah-form.rst +60 -0
  13. python_netgear_switch_library-0.0.post329/docs/protocols/http/cheetah-v1.rst +57 -0
  14. python_netgear_switch_library-0.0.post329/docs/protocols/http/gambit.rst +49 -0
  15. python_netgear_switch_library-0.0.post329/docs/protocols/http/merge-hash-cgi.rst +59 -0
  16. python_netgear_switch_library-0.0.post329/docs/protocols/http/xml-api.rst +54 -0
  17. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/protocols/http.rst +24 -46
  18. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/_version.py +2 -2
  19. python_netgear_switch_library-0.0.post329/tests/test_docs_coverage.py +97 -0
  20. python_netgear_switch_library-0.0.post326/docs/_static/ngsw.css +0 -27
  21. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/.github/workflows/ci.yml +0 -0
  22. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/.github/workflows/deb.yml +0 -0
  23. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/.github/workflows/publish-pypi.yml +0 -0
  24. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/.gitignore +0 -0
  25. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/.readthedocs.yaml +0 -0
  26. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/CLAUDE.md +0 -0
  27. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/LICENSE +0 -0
  28. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/README.md +0 -0
  29. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/RELEASING.md +0 -0
  30. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/debian/changelog +0 -0
  31. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/debian/control +0 -0
  32. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/debian/copyright +0 -0
  33. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/debian/rules +0 -0
  34. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/debian/source/format +0 -0
  35. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/Makefile +0 -0
  36. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/_ext/filelinks.py +0 -0
  37. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/cli.rst +0 -0
  38. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/core.rst +0 -0
  39. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/facades.rst +0 -0
  40. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/index.rst +0 -0
  41. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/mcp.rst +0 -0
  42. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/operations.rst +0 -0
  43. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/protocols.rst +0 -0
  44. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/transport.rst +0 -0
  45. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/api/virtual.rst +0 -0
  46. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/cli.rst +0 -0
  47. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/conf.py +0 -0
  48. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/development.rst +0 -0
  49. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/fake/index.rst +0 -0
  50. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/fake/internals.rst +0 -0
  51. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/fake/serving.rst +0 -0
  52. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/fake/testing.rst +0 -0
  53. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/concepts.rst +0 -0
  54. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/configuration.rst +0 -0
  55. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/installation.rst +0 -0
  56. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/principles.rst +0 -0
  57. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/quickstart.rst +0 -0
  58. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/reading.rst +0 -0
  59. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/guide/writing.rst +0 -0
  60. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/index.rst +0 -0
  61. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/mcp.rst +0 -0
  62. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/index.rst +0 -0
  63. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/models/support.rst +0 -0
  64. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/protocols/cli.rst +0 -0
  65. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/protocols/index.rst +0 -0
  66. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/protocols/nsdp.rst +0 -0
  67. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/protocols/snmp.rst +0 -0
  68. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
  69. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
  70. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
  71. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
  72. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
  73. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
  74. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
  75. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
  76. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
  77. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
  78. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
  79. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/packaging/apt-index.html +0 -0
  80. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/packaging/ci-build.sh +0 -0
  81. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/packaging/deb-version.py +0 -0
  82. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/pyproject.toml +0 -0
  83. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/__init__.py +0 -0
  84. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/_dispatch.py +0 -0
  85. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/aio_api.py +0 -0
  86. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/capabilities.py +0 -0
  87. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/__init__.py +0 -0
  88. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/capture.py +0 -0
  89. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/context.py +0 -0
  90. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/format.py +0 -0
  91. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/main.py +0 -0
  92. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/resolve.py +0 -0
  93. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli/safety.py +0 -0
  94. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli_read.py +0 -0
  95. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/cli_write.py +0 -0
  96. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/config.py +0 -0
  97. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/errors.py +0 -0
  98. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/http_read.py +0 -0
  99. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/http_write.py +0 -0
  100. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/mcp/__init__.py +0 -0
  101. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/mcp/server.py +0 -0
  102. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/models.py +0 -0
  103. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/nsdp_read.py +0 -0
  104. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/nsdp_write.py +0 -0
  105. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/__init__.py +0 -0
  106. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/cli/__init__.py +0 -0
  107. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/cli/commands.py +0 -0
  108. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/cli/parse.py +0 -0
  109. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/__init__.py +0 -0
  110. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/crypt.py +0 -0
  111. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/endpoints.py +0 -0
  112. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/forms.py +0 -0
  113. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/parse.py +0 -0
  114. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/session.py +0 -0
  115. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/http/types.py +0 -0
  116. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
  117. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
  118. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/client.py +0 -0
  119. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
  120. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
  121. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/types.py +0 -0
  122. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/nsdp/write.py +0 -0
  123. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
  124. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/snmp/client.py +0 -0
  125. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/snmp/oids.py +0 -0
  126. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/snmp/parse.py +0 -0
  127. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/protocols/snmp/write.py +0 -0
  128. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/py.typed +0 -0
  129. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/registry.py +0 -0
  130. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/snmp_read.py +0 -0
  131. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/snmp_write.py +0 -0
  132. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/sync_api.py +0 -0
  133. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/__init__.py +0 -0
  134. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/aio/__init__.py +0 -0
  135. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
  136. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
  137. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/cli/__init__.py +0 -0
  138. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/cli/console.py +0 -0
  139. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/cli/session.py +0 -0
  140. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/cli/ssh.py +0 -0
  141. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/cli/telnet.py +0 -0
  142. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/http/__init__.py +0 -0
  143. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/http/client.py +0 -0
  144. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/sync/__init__.py +0 -0
  145. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
  146. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
  147. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/__init__.py +0 -0
  148. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/cli_fastpath.py +0 -0
  149. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/faces/__init__.py +0 -0
  150. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/faces/cli.py +0 -0
  151. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/faces/http.py +0 -0
  152. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/faces/mibview.py +0 -0
  153. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
  154. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/faces/snmp.py +0 -0
  155. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/seed.py +0 -0
  156. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/server.py +0 -0
  157. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/state.py +0 -0
  158. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web.py +0 -0
  159. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
  160. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
  161. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
  162. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
  163. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
  164. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
  165. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
  166. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
  167. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/src/netgear_switch/virtual/web_m4300.py +0 -0
  168. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/capture_parity.py +0 -0
  169. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_capture.py +0 -0
  170. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_cli_integration.py +0 -0
  171. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_control_poe_port.py +0 -0
  172. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_control_vlan_ip.py +0 -0
  173. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_format.py +0 -0
  174. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_main_framework.py +0 -0
  175. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_new_commands.py +0 -0
  176. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_op_coverage.py +0 -0
  177. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_prompt_wiring.py +0 -0
  178. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_read_commands.py +0 -0
  179. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_resolve.py +0 -0
  180. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_safety.py +0 -0
  181. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/cli/test_serve.py +0 -0
  182. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/conftest.py +0 -0
  183. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/equivalence.py +0 -0
  184. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/captures/gsm7228ps.json +0 -0
  185. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/captures/gsm7252ps.json +0 -0
  186. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/captures/m4300-16x.json +0 -0
  187. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/captures/m4300-24x.json +0 -0
  188. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
  189. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
  190. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
  191. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
  192. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
  193. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
  194. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
  195. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
  196. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
  197. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
  198. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
  199. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
  200. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
  201. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
  202. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
  203. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
  204. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
  205. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
  206. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
  207. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
  208. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
  209. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
  210. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
  211. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
  212. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
  213. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
  214. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
  215. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
  216. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
  217. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
  218. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
  219. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
  220. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
  221. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
  222. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
  223. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
  224. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
  225. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
  226. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
  227. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
  228. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
  229. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
  230. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
  231. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
  232. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
  233. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs105pe_membership.html +0 -0
  234. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs105pe_portstats.html +0 -0
  235. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs105pe_pvid.html +0 -0
  236. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs105pe_status.html +0 -0
  237. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
  238. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
  239. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
  240. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
  241. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_login.html +0 -0
  242. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
  243. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_pvid.html +0 -0
  244. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_redirect.html +0 -0
  245. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
  246. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
  247. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
  248. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_login.html +0 -0
  249. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_membership.html +0 -0
  250. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
  251. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_portstats.html +0 -0
  252. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_pvid.html +0 -0
  253. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
  254. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
  255. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
  256. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
  257. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
  258. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
  259. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
  260. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
  261. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
  262. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
  263. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
  264. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
  265. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
  266. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
  267. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
  268. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
  269. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
  270. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
  271. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
  272. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
  273. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
  274. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
  275. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
  276. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_login.html +0 -0
  277. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
  278. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
  279. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
  280. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
  281. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
  282. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
  283. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
  284. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
  285. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
  286. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
  287. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
  288. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
  289. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
  290. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
  291. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_addresstable.html +0 -0
  292. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
  293. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
  294. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_ports.html +0 -0
  295. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_portstats.html +0 -0
  296. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_pvid.html +0 -0
  297. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_sysinfo.html +0 -0
  298. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
  299. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
  300. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
  301. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
  302. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
  303. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
  304. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/http_specs.py +0 -0
  305. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/cli/test_cli_parse.py +0 -0
  306. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/http/test_crypt.py +0 -0
  307. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/http/test_endpoints.py +0 -0
  308. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/http/test_forms.py +0 -0
  309. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/http/test_parse.py +0 -0
  310. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/nsdp/test_auth.py +0 -0
  311. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/nsdp/test_parsers.py +0 -0
  312. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/nsdp/test_protocol.py +0 -0
  313. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/nsdp/test_write_frame.py +0 -0
  314. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_client.py +0 -0
  315. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_oids.py +0 -0
  316. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
  317. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
  318. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
  319. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_parse_ports.py +0 -0
  320. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_parse_system_info.py +0 -0
  321. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_parse_vlans.py +0 -0
  322. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/protocols/snmp/test_write_encode.py +0 -0
  323. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_aio_api.py +0 -0
  324. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_capabilities.py +0 -0
  325. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_cli_read.py +0 -0
  326. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_cli_vlan_write.py +0 -0
  327. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_cli_write.py +0 -0
  328. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_config.py +0 -0
  329. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_cross_backend_equivalence.py +0 -0
  330. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_deb_version.py +0 -0
  331. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_dispatch.py +0 -0
  332. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_errors.py +0 -0
  333. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_facade_equivalence.py +0 -0
  334. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_http_dispatch.py +0 -0
  335. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_http_equivalence.py +0 -0
  336. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_http_read.py +0 -0
  337. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_http_vlan_membership.py +0 -0
  338. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_http_write.py +0 -0
  339. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_http_xui_writes.py +0 -0
  340. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_import.py +0 -0
  341. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_mcp_server.py +0 -0
  342. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_models.py +0 -0
  343. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_models_snmp_read.py +0 -0
  344. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_nsdp_integration.py +0 -0
  345. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_nsdp_read.py +0 -0
  346. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_nsdp_write.py +0 -0
  347. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_public_api.py +0 -0
  348. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_quality_gates.py +0 -0
  349. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_registry.py +0 -0
  350. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_snmp_integration.py +0 -0
  351. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_snmp_read.py +0 -0
  352. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_snmp_write.py +0 -0
  353. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_sync_api.py +0 -0
  354. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_version.py +0 -0
  355. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/test_write_equivalence.py +0 -0
  356. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/_fastpath_fake.py +0 -0
  357. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_cli_console.py +0 -0
  358. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_cli_scp_cert.py +0 -0
  359. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_cli_telnet.py +0 -0
  360. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_http_client.py +0 -0
  361. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_nsdp_udp_sync.py +0 -0
  362. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_set_transport.py +0 -0
  363. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
  364. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_snmp_pysnmp.py +0 -0
  365. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/transport/test_value_parity.py +0 -0
  366. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_cli_cross_backend.py +0 -0
  367. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
  368. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_gsm7228ps_seed.py +0 -0
  369. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_m4300_seeds.py +0 -0
  370. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_mibview.py +0 -0
  371. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_mutable_state.py +0 -0
  372. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_nsdp_state.py +0 -0
  373. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_serve_daemon.py +0 -0
  374. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_snmp_write_face.py +0 -0
  375. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_state_seed.py +0 -0
  376. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_switchport_vlan_write.py +0 -0
  377. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_virtual_http_face.py +0 -0
  378. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_virtual_nsdp_face.py +0 -0
  379. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_virtual_snmp_face.py +0 -0
  380. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/tests/virtual/test_web_projection.py +0 -0
  381. {python_netgear_switch_library-0.0.post326 → python_netgear_switch_library-0.0.post329}/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.post326
3
+ Version: 0.0.post329
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
@@ -32,9 +32,12 @@ from __future__ import annotations
32
32
  from pathlib import Path
33
33
  from typing import TYPE_CHECKING, Any, ClassVar
34
34
 
35
+ from docutils import nodes
35
36
  from docutils.parsers.rst import directives
37
+ from sphinx.transforms.post_transforms import SphinxPostTransform
36
38
  from sphinx.util import logging
37
39
  from sphinx.util.docutils import SphinxDirective
40
+ from sphinx.util.nodes import make_refnode
38
41
 
39
42
  from netgear_switch.capabilities import (
40
43
  READ_OPERATIONS,
@@ -43,12 +46,12 @@ from netgear_switch.capabilities import (
43
46
  backends_for,
44
47
  support,
45
48
  )
49
+ from netgear_switch.protocols.http.endpoints import HTTP_SPECS
46
50
  from netgear_switch.registry import MODEL_ALIASES, MODELS, Backend, get_model
47
51
 
48
52
  if TYPE_CHECKING:
49
53
  from collections.abc import Iterable, Sequence
50
54
 
51
- from docutils import nodes
52
55
  from sphinx.application import Sphinx
53
56
 
54
57
  logger = logging.getLogger(__name__)
@@ -87,6 +90,151 @@ def unverified_models() -> tuple[str, ...]:
87
90
  return tuple(key for key, model in MODELS.items() if not model.verified)
88
91
 
89
92
 
93
+ #: The page documenting each backend. SSH, TELNET and CONSOLE are three
94
+ #: transports over one command surface, so all three land on the CLI page.
95
+ _BACKEND_PAGES = {
96
+ Backend.SNMP: "protocols/snmp",
97
+ Backend.NSDP: "protocols/nsdp",
98
+ Backend.HTTP: "protocols/http",
99
+ Backend.SSH: "protocols/cli",
100
+ Backend.TELNET: "protocols/cli",
101
+ Backend.CONSOLE: "protocols/cli",
102
+ }
103
+
104
+ #: Each web-UI login scheme gets its own page. They share a transport and
105
+ #: nothing else: different login mechanics, different session handling and
106
+ #: different page sets, which is why one "HTTP" page could not describe them.
107
+ _HTTP_SCHEME_PAGES = {
108
+ "MERGE_HASH_CGI": "protocols/http/merge-hash-cgi",
109
+ "GAMBIT": "protocols/http/gambit",
110
+ "CHEETAH_FORM": "protocols/http/cheetah-form",
111
+ "CHEETAH_V1": "protocols/http/cheetah-v1",
112
+ "XML_API": "protocols/http/xml-api",
113
+ }
114
+
115
+ #: Literal text -> the page that documents it. Everything a generated table or a
116
+ #: docstring can name: every model key, every alias, every backend name, every
117
+ #: web-UI login scheme, and ``CLI`` for the command surface as a whole.
118
+ _LINKABLE: dict[str, str] = {
119
+ **{key: f"models/{key}" for key in MODELS},
120
+ **{alias: f"models/{key}" for alias, key in MODEL_ALIASES.items()},
121
+ **{backend.name: page for backend, page in _BACKEND_PAGES.items()},
122
+ **_HTTP_SCHEME_PAGES,
123
+ "CLI": "protocols/cli",
124
+ }
125
+
126
+
127
+ class ReferenceLinks(SphinxPostTransform):
128
+ """Link every inline literal that names something this documentation defines.
129
+
130
+ Three kinds of name are resolved, in order: a switch model key or alias, a
131
+ backend name, and any Python object autodoc has documented (matched on its
132
+ full dotted path, so there is no ambiguity to guess at).
133
+
134
+ Doing this in a post-transform rather than at each use is what makes the
135
+ linking total, and it exists because per-use linking kept missing cases:
136
+
137
+ * The generated tables wrote a model key as a bare literal in three of the
138
+ seven builders, so ``docs/models/index.rst`` listed eight model keys and
139
+ linked none of them, while the protocol pages linked theirs.
140
+ * ``conf.py`` sets ``default_role = "literal"``, so every single-backtick
141
+ name renders as text. The module lists at the bottom of each protocol
142
+ page are written that way -- ```netgear_switch.http_read``` -- and were
143
+ silently not links, even though the class names beside them, written with
144
+ an explicit role, were.
145
+
146
+ Neither failure could be caught by ``nitpicky``: an unresolved *reference*
147
+ fails the build, but a literal that should have been a reference is
148
+ indistinguishable from ordinary text. Only linking them automatically
149
+ closes that gap.
150
+
151
+ Matching is exact and confined to inline literals, so prose about "the HTTP
152
+ backend" is untouched while ``` ``HTTP`` ``` becomes a link.
153
+ """
154
+
155
+ # After filelinks' post-transform (400), whose repository links are already
156
+ # `reference` nodes by this point and are skipped below.
157
+ default_priority = 410
158
+
159
+ #: The facade a bare operation name should resolve to when several classes
160
+ #: define it. ``get_vlans`` is documented on nine classes -- both facades and
161
+ #: every reader -- and the one a reader of prose means is the public
162
+ #: synchronous entry point.
163
+ _PREFERRED_OWNER = "netgear_switch.sync_api.SyncSwitch."
164
+
165
+ def _short_index(self) -> dict[str, list[str]]:
166
+ """Last path component -> the full names that end with it."""
167
+ objects = self.env.domaindata.get("py", {}).get("objects", {})
168
+ index: dict[str, list[str]] = {}
169
+ for name, entry in objects.items():
170
+ if getattr(entry, "aliased", False):
171
+ continue # a duplicate of the canonical entry
172
+ index.setdefault(name.rsplit(".", 1)[-1], []).append(name)
173
+ return index
174
+
175
+ def _python_target(self, text: str) -> tuple[str, str] | None:
176
+ """``(docname, node_id)`` for a documented Python object, if any.
177
+
178
+ An exact dotted path always wins. A bare name is resolved only when it
179
+ is shaped like an API name -- CamelCase, SCREAMING_CASE or containing an
180
+ underscore -- and resolves unambiguously, or to the preferred facade.
181
+
182
+ The shape test is what keeps the linking honest. ``port``, ``name``,
183
+ ``host`` and ``model`` are all documented dataclass fields *and* ordinary
184
+ words that appear as literals throughout these docstrings; linking every
185
+ occurrence to one arbitrary dataclass would be worse than not linking.
186
+ """
187
+ objects = self.env.domaindata.get("py", {}).get("objects", {})
188
+ entry = objects.get(text)
189
+ if entry is not None:
190
+ return entry.docname, entry.node_id
191
+ if not ("_" in text or (text[:1].isupper() and text.isalnum())):
192
+ return None
193
+ candidates = self._short_index().get(text)
194
+ if not candidates:
195
+ return None
196
+ if len(candidates) > 1:
197
+ preferred = [c for c in candidates if c.startswith(self._PREFERRED_OWNER)]
198
+ if len(preferred) != 1:
199
+ return None
200
+ candidates = preferred
201
+ entry = objects[candidates[0]]
202
+ return entry.docname, entry.node_id
203
+
204
+ def run(self, **kwargs: Any) -> None:
205
+ counts = self.env.domaindata.setdefault("ngsw_reflinks", {"linked": 0})
206
+ for node in list(self.document.findall(nodes.literal)):
207
+ if isinstance(node.parent, nodes.reference):
208
+ continue # already a link (a resolved xref, or filelinks')
209
+ if "xref" in node["classes"]:
210
+ continue # an unresolved cross-reference, not a plain literal
211
+ text = node.astext().strip()
212
+
213
+ docname = _LINKABLE.get(text)
214
+ if docname is not None and docname in self.env.found_docs:
215
+ node_id = ""
216
+ else:
217
+ found = self._python_target(text)
218
+ if found is None:
219
+ continue
220
+ docname, node_id = found
221
+
222
+ # A page linking to itself is noise; an anchor on the same page is
223
+ # not -- it jumps to the definition.
224
+ if docname == self.env.docname and not node_id:
225
+ continue
226
+ link = make_refnode(
227
+ self.app.builder,
228
+ self.env.docname,
229
+ docname,
230
+ node_id or None,
231
+ node.deepcopy(),
232
+ )
233
+ link["classes"].append("ngsw-ref")
234
+ node.replace_self(link)
235
+ counts["linked"] += 1
236
+
237
+
90
238
  def _rst_escape(text: str) -> str:
91
239
  """Neutralise inline reST markup in generated cell text."""
92
240
  for char in ("\\", "*", "`", "|", "_"):
@@ -99,14 +247,22 @@ def _list_table(
99
247
  header: Sequence[str],
100
248
  rows: Iterable[Sequence[str]],
101
249
  *,
102
- widths: str = "",
250
+ classes: Sequence[str] = (),
103
251
  ) -> str:
104
- """Render a reST ``list-table`` block."""
252
+ """Render a reST ``list-table`` block.
253
+
254
+ No ``:widths:`` is emitted, deliberately. ``docs/_static/ngsw.css`` gives
255
+ these tables ``white-space: nowrap`` inside a horizontally scrolling box,
256
+ so the browser's own table layout sizes every column to its content --
257
+ which is always right, and cannot go stale the way a hand-picked
258
+ percentage does when a model name or a backend list changes.
259
+
260
+ ``classes`` adds CSS classes beyond ``ngsw-support-table``. Only a table
261
+ whose last column holds prose wants ``ngsw-reason-table``.
262
+ """
105
263
  out = [f".. list-table:: {title}" if title else ".. list-table::"]
106
264
  out.append(" :header-rows: 1")
107
- if widths:
108
- out.append(f" :widths: {widths}")
109
- out.append(" :class: ngsw-support-table")
265
+ out.append(" :class: " + " ".join(("ngsw-support-table", *classes)))
110
266
  out.append("")
111
267
  for row in (header, *rows):
112
268
  cells = list(row)
@@ -149,7 +305,7 @@ class ModelTable(_GeneratedTable):
149
305
  model.switch_class.value.replace("_", " "),
150
306
  str(model.port_count),
151
307
  str(model.poe_port_count),
152
- ", ".join(b.name for b in backends_for(model)),
308
+ ", ".join(f"``{b.name}``" for b in backends_for(model)),
153
309
  "live-verified" if model.verified else "**unverified**",
154
310
  ]
155
311
  )
@@ -165,7 +321,6 @@ class ModelTable(_GeneratedTable):
165
321
  "Status",
166
322
  ],
167
323
  rows,
168
- widths="14 18 14 7 8 22 17",
169
324
  )
170
325
 
171
326
 
@@ -188,9 +343,8 @@ class ProtocolTable(_GeneratedTable):
188
343
  rows.append([f"``{key}``", *cells])
189
344
  return _list_table(
190
345
  "",
191
- ["Model", *(b.name for b in self._COLUMNS)],
346
+ ["Model", *(f"``{b.name}``" for b in self._COLUMNS)],
192
347
  rows,
193
- widths="26 12 12 12 12 12",
194
348
  )
195
349
 
196
350
 
@@ -259,7 +413,7 @@ class ModelSupportTable(_GeneratedTable):
259
413
 
260
414
  table = _list_table(
261
415
  "",
262
- ["Operation", "What it does", *(b.name for b in backends)],
416
+ ["Operation", "What it does", *(f"``{b.name}``" for b in backends)],
263
417
  rows,
264
418
  )
265
419
  if not notes:
@@ -302,7 +456,7 @@ class SupportGaps(_GeneratedTable):
302
456
  "",
303
457
  ["Model", "Operation", "Served by", "Not served by", "Why"],
304
458
  rows,
305
- widths="12 16 12 14 46",
459
+ classes=("ngsw-reason-table",),
306
460
  )
307
461
 
308
462
 
@@ -342,13 +496,11 @@ class BackendModelTable(_GeneratedTable):
342
496
  for key in _models_with(backends):
343
497
  model = MODELS[key]
344
498
  reached = ", ".join(
345
- b.name for b in backends_for(model) if b in backends
499
+ f"``{b.name}``" for b in backends_for(model) if b in backends
346
500
  )
347
501
  rows.append(
348
502
  [
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}>`",
503
+ f"``{key}``",
352
504
  _rst_escape(model.display_name),
353
505
  model.switch_class.value.replace("_", " "),
354
506
  str(model.port_count),
@@ -361,7 +513,6 @@ class BackendModelTable(_GeneratedTable):
361
513
  "",
362
514
  ["Model", "Product", "Class", "Ports", "Reached over"],
363
515
  rows,
364
- widths="16 24 18 10 32",
365
516
  )
366
517
 
367
518
 
@@ -382,9 +533,7 @@ class BackendOperationTable(_GeneratedTable):
382
533
  for op in (*READ_OPERATIONS, *WRITE_OPERATIONS):
383
534
  cells = []
384
535
  for key in keys:
385
- backend = next(
386
- (b for b in backends_for(key) if b in backends), None
387
- )
536
+ backend = next((b for b in backends_for(key) if b in backends), None)
388
537
  if backend is None: # pragma: no cover - filtered by _models_with
389
538
  cells.append(_NO)
390
539
  continue
@@ -414,6 +563,136 @@ class BackendOperationTable(_GeneratedTable):
414
563
  return f"{table}\n{citations}\n"
415
564
 
416
565
 
566
+ #: Every page an ``HttpModelSpec`` can carry a path for, in the order a reader
567
+ #: meets them. ``None`` on a spec means that firmware ships no such page, which
568
+ #: is why the generated coverage table can show a real gap rather than implying
569
+ #: the operation is merely unimplemented here.
570
+ _HTTP_PAGE_FIELDS = (
571
+ ("Dashboard / port status", "dashboard_path"),
572
+ ("Port statistics", "stats_path"),
573
+ ("PoE status", "poe_status_path"),
574
+ ("PoE configuration", "poe_config_path"),
575
+ ("VLAN configuration", "vlan_config_path"),
576
+ ("VLAN membership", "vlan_membership_path"),
577
+ ("PVIDs", "pvid_path"),
578
+ ("MAC / FDB table", "mac_table_path"),
579
+ ("LLDP neighbours", "lldp_path"),
580
+ ("System info", "sysinfo_path"),
581
+ ("Management IP", "mgmt_ip_path"),
582
+ ("Port configuration", "port_config_path"),
583
+ ("Certificate upload", "cert_upload_path"),
584
+ ("Reboot", "reboot_path"),
585
+ ("Logout", "logout_path"),
586
+ )
587
+
588
+
589
+ def _scheme_specs(scheme: str) -> list[Any]:
590
+ return [s for s in HTTP_SPECS.values() if s.scheme.name == scheme]
591
+
592
+
593
+ def _transport(spec: Any) -> str:
594
+ """How the web UI is reached: scheme and port, where they are not default."""
595
+ proto = "HTTPS" if spec.secure else "HTTP"
596
+ return f"{proto}, port {spec.web_port}" if spec.web_port else proto
597
+
598
+
599
+ class HttpSchemeTable(_GeneratedTable):
600
+ """Every web-UI login scheme, with the models and dialects behind it."""
601
+
602
+ def body(self) -> str:
603
+ rows = []
604
+ for scheme, page in _HTTP_SCHEME_PAGES.items():
605
+ specs = _scheme_specs(scheme)
606
+ if not specs: # pragma: no cover - every scheme has a model today
607
+ continue
608
+ dialects = sorted({s.html_dialect.name for s in specs})
609
+ rows.append(
610
+ [
611
+ f":doc:`{scheme} </{page}>`",
612
+ ", ".join(f"``{s.model_key}``" for s in specs),
613
+ ", ".join(f"``{d}``" for d in dialects),
614
+ _transport(specs[0]),
615
+ ]
616
+ )
617
+ return _list_table(
618
+ "",
619
+ ["Login scheme", "Switches", "HTML dialect", "Reached over"],
620
+ rows,
621
+ )
622
+
623
+
624
+ class HttpSchemeFacts(_GeneratedTable):
625
+ """One login scheme: the models on it and how each one is talked to."""
626
+
627
+ required_arguments = 1
628
+
629
+ def body(self) -> str:
630
+ scheme = self.arguments[0].strip().upper()
631
+ specs = _scheme_specs(scheme)
632
+ if not specs:
633
+ raise self.error(f"ngsw-http-scheme: no model uses scheme {scheme!r}")
634
+ rows = []
635
+ for spec in specs:
636
+ if spec.session_token_field:
637
+ session = f"token, ``{spec.session_token_field}``"
638
+ elif spec.cookie_name:
639
+ session = f"cookie, ``{spec.cookie_name}``"
640
+ else:
641
+ session = "none"
642
+ rows.append(
643
+ [
644
+ f"``{spec.model_key}``",
645
+ f"``{spec.html_dialect.name}``",
646
+ _transport(spec),
647
+ f"``{spec.login_path}``",
648
+ ", ".join(
649
+ f"``{f}``"
650
+ for f in (spec.username_field, spec.password_field)
651
+ if f
652
+ ),
653
+ session,
654
+ ]
655
+ )
656
+ return _list_table(
657
+ "",
658
+ [
659
+ "Switch",
660
+ "HTML dialect",
661
+ "Reached over",
662
+ "Login path",
663
+ "Fields",
664
+ "Session",
665
+ ],
666
+ rows,
667
+ )
668
+
669
+
670
+ class HttpSchemePages(_GeneratedTable):
671
+ """Which pages each model on a scheme actually ships."""
672
+
673
+ required_arguments = 1
674
+
675
+ def body(self) -> str:
676
+ scheme = self.arguments[0].strip().upper()
677
+ specs = _scheme_specs(scheme)
678
+ if not specs:
679
+ raise self.error(f"ngsw-http-scheme-pages: no model uses {scheme!r}")
680
+ rows = []
681
+ for label, field in _HTTP_PAGE_FIELDS:
682
+ cells = []
683
+ for spec in specs:
684
+ path = getattr(spec, field, None)
685
+ cells.append(f"``{path}``" if path else _NO)
686
+ if all(cell == _NO for cell in cells):
687
+ continue
688
+ rows.append([label, *cells])
689
+ return _list_table(
690
+ "",
691
+ ["Page", *(f"``{s.model_key}``" for s in specs)],
692
+ rows,
693
+ )
694
+
695
+
417
696
  class ModelFacts(_GeneratedTable):
418
697
  """The registry record for one model, as a field list."""
419
698
 
@@ -450,16 +729,14 @@ class ModelFacts(_GeneratedTable):
450
729
  ]
451
730
  )
452
731
  if aliases:
453
- rows.append(
454
- ["Aliases", ", ".join(f"``{a}``" for a in aliases)]
455
- )
732
+ rows.append(["Aliases", ", ".join(f"``{a}``" for a in aliases)])
456
733
  rows.append(
457
734
  [
458
735
  "MAC/FDB table",
459
736
  "yes" if model.has_mac_table else "no",
460
737
  ]
461
738
  )
462
- return _list_table("", ["Field", "Value"], rows, widths="30 70")
739
+ return _list_table("", ["Field", "Value"], rows)
463
740
 
464
741
 
465
742
  class ModelPhoto(_GeneratedTable):
@@ -497,6 +774,71 @@ class ModelPhoto(_GeneratedTable):
497
774
  MISSING_PHOTOS: set[str] = set()
498
775
 
499
776
 
777
+ class ModelDiagram(_GeneratedTable):
778
+ """A schematic port map for one model, drawn from the registry.
779
+
780
+ Not a photograph and not a faceplate: it shows the ports *as this library
781
+ addresses them*, which is the thing a caller actually needs when deciding
782
+ what to pass as ``port=``. Generated from ``port_count`` and
783
+ ``poe_port_count``, so it cannot drift from the registry -- and so it never
784
+ asserts a physical arrangement nobody measured.
785
+ """
786
+
787
+ required_arguments = 1
788
+
789
+ _COLS = 26
790
+ _BOX = 26
791
+ _GAP = 4
792
+
793
+ def body(self) -> str:
794
+ model = get_model(self.arguments[0])
795
+ total, poe = model.port_count, model.poe_port_count
796
+ step = self._BOX + self._GAP
797
+ rows = (total + self._COLS - 1) // self._COLS
798
+ width = min(total, self._COLS) * step + self._GAP
799
+ height = rows * step + self._GAP + 22
800
+
801
+ parts = [
802
+ f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {height}" '
803
+ f'role="img" aria-label="Port map: {total} ports, {poe} with PoE" '
804
+ 'style="max-width:100%;height:auto">'
805
+ ]
806
+ for index in range(total):
807
+ port = index + 1
808
+ col, row = index % self._COLS, index // self._COLS
809
+ x = self._GAP + col * step
810
+ y = self._GAP + row * step
811
+ # PoE ports come first on every model in this registry; anything
812
+ # beyond that count is drawn as a plain (usually uplink) port.
813
+ fill = "#2b6cb0" if port <= poe else "#a0aec0"
814
+ parts.append(
815
+ f'<rect x="{x}" y="{y}" width="{self._BOX}" height="{self._BOX}" '
816
+ f'rx="3" fill="{fill}"/>'
817
+ f'<text x="{x + self._BOX / 2}" y="{y + self._BOX / 2 + 4}" '
818
+ 'font-size="10" text-anchor="middle" fill="#fff" '
819
+ f'font-family="monospace">{port}</text>'
820
+ )
821
+ legend_y = rows * step + self._GAP + 14
822
+ parts.append(
823
+ f'<rect x="4" y="{legend_y - 9}" width="11" height="11" rx="2" '
824
+ 'fill="#2b6cb0"/>'
825
+ f'<text x="20" y="{legend_y}" font-size="11" fill="currentColor">'
826
+ f"PoE ({poe})</text>"
827
+ f'<rect x="86" y="{legend_y - 9}" width="11" height="11" rx="2" '
828
+ 'fill="#a0aec0"/>'
829
+ f'<text x="102" y="{legend_y}" font-size="11" fill="currentColor">'
830
+ f"no PoE ({total - poe})</text>"
831
+ )
832
+ parts.append("</svg>")
833
+ svg = "".join(parts)
834
+ return (
835
+ ".. raw:: html\n\n"
836
+ f' <figure class="ngsw-port-map">{svg}'
837
+ "<figcaption>Port map as the library addresses these ports — "
838
+ "schematic, not a faceplate layout.</figcaption></figure>\n"
839
+ )
840
+
841
+
500
842
  class UnverifiedNote(_GeneratedTable):
501
843
  """Name the models the tables leave out, and say why."""
502
844
 
@@ -535,6 +877,13 @@ def _report_missing_photos(app: Sphinx, exception: Exception | None) -> None:
535
877
  )
536
878
 
537
879
 
880
+ def _report_reference_links(app: Sphinx, exception: Exception | None) -> None:
881
+ if exception is not None:
882
+ return
883
+ linked = app.env.domaindata.get("ngsw_reflinks", {}).get("linked", 0)
884
+ logger.info("ngsw: linked %d model/backend/API reference(s)", linked)
885
+
886
+
538
887
  def setup(app: Sphinx) -> dict[str, Any]:
539
888
  app.add_directive("ngsw-unverified-note", UnverifiedNote)
540
889
  app.add_directive("ngsw-model-table", ModelTable)
@@ -543,8 +892,14 @@ def setup(app: Sphinx) -> dict[str, Any]:
543
892
  app.add_directive("ngsw-model-support", ModelSupportTable)
544
893
  app.add_directive("ngsw-model-facts", ModelFacts)
545
894
  app.add_directive("ngsw-model-photo", ModelPhoto)
895
+ app.add_directive("ngsw-model-diagram", ModelDiagram)
546
896
  app.add_directive("ngsw-backend-models", BackendModelTable)
547
897
  app.add_directive("ngsw-backend-operations", BackendOperationTable)
548
898
  app.add_directive("ngsw-support-gaps", SupportGaps)
899
+ app.add_directive("ngsw-http-scheme-table", HttpSchemeTable)
900
+ app.add_directive("ngsw-http-scheme", HttpSchemeFacts)
901
+ app.add_directive("ngsw-http-scheme-pages", HttpSchemePages)
902
+ app.add_post_transform(ReferenceLinks)
549
903
  app.connect("build-finished", _report_missing_photos)
904
+ app.connect("build-finished", _report_reference_links)
550
905
  return {"version": "1.0", "parallel_read_safe": True, "parallel_write_safe": True}
@@ -0,0 +1,44 @@
1
+ /* Generated support tables are wide by nature -- one column per switch model.
2
+ Let them scroll inside their own box rather than stretch the page. */
3
+ table.ngsw-support-table {
4
+ display: block;
5
+ overflow-x: auto;
6
+ max-width: 100%;
7
+ white-space: nowrap;
8
+ }
9
+
10
+ table.ngsw-support-table td,
11
+ table.ngsw-support-table th {
12
+ vertical-align: top;
13
+ }
14
+
15
+ /* A table whose LAST column carries full sentences (the parity-gaps "Why"
16
+ column): let it wrap, and give it room to be worth wrapping.
17
+
18
+ Scoped to that one class deliberately. As a blanket
19
+ `.ngsw-support-table td:last-child` rule this forced 20rem onto the last
20
+ column of every generated table -- so the model table's Status column, whose
21
+ widest value is the 13-character "live-verified", was rendered 320px wide. */
22
+ table.ngsw-reason-table td:last-child {
23
+ white-space: normal;
24
+ min-width: 20rem;
25
+ }
26
+
27
+ /* Links produced by docs/_ext/filelinks.py: a file path that is also a link.
28
+ Keep the monospace look, add the underline links normally get. */
29
+ a.repofile code,
30
+ a.repofile tt {
31
+ text-decoration: underline;
32
+ text-decoration-style: dotted;
33
+ }
34
+
35
+ /* Generated port maps (docs/_ext/support_tables.py's ngsw-model-diagram). */
36
+ figure.ngsw-port-map {
37
+ margin: 1rem 0 1.5rem;
38
+ text-align: center;
39
+ }
40
+ figure.ngsw-port-map figcaption {
41
+ font-size: 0.85em;
42
+ opacity: 0.75;
43
+ margin-top: 0.4rem;
44
+ }
@@ -3,6 +3,8 @@ GS105PE
3
3
 
4
4
  .. ngsw-model-photo:: gs105pe
5
5
 
6
+ .. ngsw-model-diagram:: gs105pe
7
+
6
8
  A 5-port "Smart Plus" switch (Gen-2 Broadcom BCM53125) — a genuinely distinct
7
9
  SKU from the GS305EP, not a rebadge. No SNMP; NSDP and the web UI.
8
10
 
@@ -3,6 +3,8 @@ GS110EMX
3
3
 
4
4
  .. ngsw-model-photo:: gs110emx
5
5
 
6
+ .. ngsw-model-diagram:: gs110emx
7
+
6
8
  A 10-port Plus switch with multi-gigabit uplinks and no PoE. Like every Plus
7
9
  model it has **no SNMP agent**: it is managed over NSDP and its web UI.
8
10
 
@@ -3,6 +3,8 @@ GS305EP
3
3
 
4
4
  .. ngsw-model-photo:: gs305ep
5
5
 
6
+ .. ngsw-model-diagram:: gs305ep
7
+
6
8
  A 5-port Plus switch with PoE on 4 ports. No SNMP; managed over NSDP and the
7
9
  web UI.
8
10
 
@@ -3,6 +3,8 @@ GS728TPP
3
3
 
4
4
  .. ngsw-model-photo:: gs728tpp
5
5
 
6
+ .. ngsw-model-diagram:: gs728tpp
7
+
6
8
  A 28-port Smart Managed Pro switch with 24 PoE+ ports. The most unusual entry in
7
9
  the registry: its SNMP agent implements **no Netgear vendor OIDs at all**, and
8
10
  its web UI is an XML API rather than scraped HTML.
@@ -3,6 +3,8 @@ S3300-52X-PoE+
3
3
 
4
4
  .. ngsw-model-photo:: gsm7228ps
5
5
 
6
+ .. ngsw-model-diagram:: gsm7228ps
7
+
6
8
  A 52-port Smart Managed Pro switch with 48 PoE ports — despite the
7
9
  ``GSM7228PS`` part-number family suggesting 28. The registry key is
8
10
  ``gsm7228ps``; ``s3300`` is an alias resolving to the same record, because the
@@ -3,6 +3,8 @@ GSM7252PS
3
3
 
4
4
  .. ngsw-model-photo:: gsm7252ps
5
5
 
6
+ .. ngsw-model-diagram:: gsm7252ps
7
+
6
8
  A 52-port Fully Managed stackable switch, 48 of them PoE. The model with the
7
9
  widest verified backend coverage in this fleet — SNMP, the XE FASTPATH web UI
8
10
  and the CLI are all cross-verified against each other — and the one the
@@ -3,6 +3,8 @@ M4300-16X
3
3
 
4
4
  .. ngsw-model-photo:: m4300-16x
5
5
 
6
+ .. ngsw-model-diagram:: m4300-16x
7
+
6
8
  The XSM4316, a Fully Managed 10G switch with PoE on all 16 ports, running
7
9
  FASTPATH **12.0.19.15** — a different firmware from its M4300-24X sibling, which
8
10
  is exactly why its behaviour was measured rather than inherited.
@@ -3,6 +3,8 @@ M4300-24X
3
3
 
4
4
  .. ngsw-model-photo:: m4300-24x
5
5
 
6
+ .. ngsw-model-diagram:: m4300-24x
7
+
6
8
  The XSM4324CS, a Fully Managed 10G aggregation switch running FASTPATH
7
9
  12.0.13.8. Reachable over SNMP, the Cheetah ``/v1`` web UI, and the FASTPATH
8
10
  CLI on SSH or telnet.