python-netgear-switch-library 0.0.post405__tar.gz → 0.1.post4__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 (428) hide show
  1. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/.github/workflows/ci.yml +4 -1
  2. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/.github/workflows/deb.yml +23 -8
  3. python_netgear_switch_library-0.1.post4/.github/workflows/publish-pypi.yml +71 -0
  4. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/PKG-INFO +4 -4
  5. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/README.md +3 -3
  6. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/RELEASING.md +29 -9
  7. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/debian/control +8 -3
  8. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/installation.rst +8 -7
  9. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/mcp.rst +28 -3
  10. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/packaging/apt-index.html +4 -4
  11. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/pyproject.toml +10 -5
  12. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/_version.py +2 -2
  13. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/mcp/server.py +85 -6
  14. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_deb_version.py +40 -11
  15. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_mcp_server.py +83 -0
  16. python_netgear_switch_library-0.0.post405/.github/workflows/publish-pypi.yml +0 -52
  17. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/.gitignore +0 -0
  18. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/.readthedocs.yaml +0 -0
  19. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/CLAUDE.md +0 -0
  20. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/LICENSE +0 -0
  21. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/debian/changelog +0 -0
  22. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/debian/copyright +0 -0
  23. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/debian/rules +0 -0
  24. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/debian/source/format +0 -0
  25. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/Makefile +0 -0
  26. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_ext/filelinks.py +0 -0
  27. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_ext/support_tables.py +0 -0
  28. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/gs105pe.png +0 -0
  29. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/gs110emx.png +0 -0
  30. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/gs305ep.png +0 -0
  31. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/gs728tpp.png +0 -0
  32. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/gsm7228ps.jpg +0 -0
  33. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/gsm7252ps.jpg +0 -0
  34. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/m4300-16x.png +0 -0
  35. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/models/m4300-24x.png +0 -0
  36. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/_static/ngsw.css +0 -0
  37. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/cli.rst +0 -0
  38. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/core.rst +0 -0
  39. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/facades.rst +0 -0
  40. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/index.rst +0 -0
  41. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/mcp.rst +0 -0
  42. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/operations.rst +0 -0
  43. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/protocols.rst +0 -0
  44. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/transport.rst +0 -0
  45. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/api/virtual.rst +0 -0
  46. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/cli.rst +0 -0
  47. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/conf.py +0 -0
  48. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/development.rst +0 -0
  49. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/fake/index.rst +0 -0
  50. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/fake/internals.rst +0 -0
  51. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/fake/serving.rst +0 -0
  52. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/fake/testing.rst +0 -0
  53. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/concepts.rst +0 -0
  54. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/configuration.rst +0 -0
  55. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/principles.rst +0 -0
  56. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/quickstart.rst +0 -0
  57. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/reading.rst +0 -0
  58. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/guide/writing.rst +0 -0
  59. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/index.rst +0 -0
  60. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/gs105pe.rst +0 -0
  61. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/gs110emx.rst +0 -0
  62. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/gs305ep.rst +0 -0
  63. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/gs728tpp.rst +0 -0
  64. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/gsm7228ps.rst +0 -0
  65. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/gsm7252ps.rst +0 -0
  66. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/index.rst +0 -0
  67. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/m4300-16x.rst +0 -0
  68. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/m4300-24x.rst +0 -0
  69. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/models/support.rst +0 -0
  70. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/cli.rst +0 -0
  71. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/http/cheetah-form.rst +0 -0
  72. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/http/cheetah-v1.rst +0 -0
  73. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/http/gambit.rst +0 -0
  74. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/http/merge-hash-cgi.rst +0 -0
  75. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/http/xml-api.rst +0 -0
  76. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/http.rst +0 -0
  77. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/index.rst +0 -0
  78. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/nsdp.rst +0 -0
  79. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/protocols/snmp.rst +0 -0
  80. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-15-foundation.md +0 -0
  81. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-17-slice-02-snmp-read-core.md +0 -0
  82. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-17-slice-03-read-apis.md +0 -0
  83. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-18-slice-04-snmp-write.md +0 -0
  84. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-18-slice-05-nsdp.md +0 -0
  85. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-18-slice-06-http.md +0 -0
  86. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-18-slice-07-cli.md +0 -0
  87. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-18-slice-08-packaging-ci.md +0 -0
  88. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/plans/2026-07-23-gsm7252ps-http-xe-dialect.md +0 -0
  89. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/specs/2026-07-15-netgear-control-library-design.md +0 -0
  90. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/specs/2026-07-23-gsm7252ps-http-xe-dialect-design.md +0 -0
  91. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/specs/2026-08-02-gs728tpp-poe-vlan-live.md +0 -0
  92. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/specs/2026-08-02-snmp-surface-findings.md +0 -0
  93. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/docs/superpowers/specs/2026-08-03-snmp-vendor-oid-sweep.md +0 -0
  94. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/packaging/ci-build.sh +0 -0
  95. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/packaging/deb-version.py +0 -0
  96. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/__init__.py +0 -0
  97. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/_dispatch.py +0 -0
  98. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/aio_api.py +0 -0
  99. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/capabilities.py +0 -0
  100. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/__init__.py +0 -0
  101. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/capture.py +0 -0
  102. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/context.py +0 -0
  103. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/format.py +0 -0
  104. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/main.py +0 -0
  105. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/resolve.py +0 -0
  106. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli/safety.py +0 -0
  107. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli_read.py +0 -0
  108. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/cli_write.py +0 -0
  109. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/config.py +0 -0
  110. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/errors.py +0 -0
  111. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/http_read.py +0 -0
  112. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/http_write.py +0 -0
  113. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/mcp/__init__.py +0 -0
  114. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/models.py +0 -0
  115. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/nsdp_read.py +0 -0
  116. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/nsdp_write.py +0 -0
  117. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/__init__.py +0 -0
  118. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/cli/__init__.py +0 -0
  119. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/cli/commands.py +0 -0
  120. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/cli/parse.py +0 -0
  121. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/__init__.py +0 -0
  122. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/crypt.py +0 -0
  123. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/endpoints.py +0 -0
  124. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/forms.py +0 -0
  125. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/goahead.py +0 -0
  126. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/parse.py +0 -0
  127. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/session.py +0 -0
  128. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/http/types.py +0 -0
  129. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/__init__.py +0 -0
  130. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/auth.py +0 -0
  131. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/client.py +0 -0
  132. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/parsers.py +0 -0
  133. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/protocol.py +0 -0
  134. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/types.py +0 -0
  135. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/nsdp/write.py +0 -0
  136. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/snmp/__init__.py +0 -0
  137. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/snmp/client.py +0 -0
  138. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/snmp/oids.py +0 -0
  139. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/snmp/parse.py +0 -0
  140. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/protocols/snmp/write.py +0 -0
  141. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/py.typed +0 -0
  142. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/registry.py +0 -0
  143. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/snmp_read.py +0 -0
  144. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/snmp_write.py +0 -0
  145. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/sync_api.py +0 -0
  146. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/__init__.py +0 -0
  147. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/aio/__init__.py +0 -0
  148. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/aio/nsdp_udp.py +0 -0
  149. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/aio/snmp_pysnmp.py +0 -0
  150. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/cli/__init__.py +0 -0
  151. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/cli/console.py +0 -0
  152. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/cli/session.py +0 -0
  153. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/cli/ssh.py +0 -0
  154. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/cli/telnet.py +0 -0
  155. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/http/__init__.py +0 -0
  156. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/http/client.py +0 -0
  157. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/sync/__init__.py +0 -0
  158. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/sync/nsdp_udp.py +0 -0
  159. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/transport/sync/snmp_netsnmp_cli.py +0 -0
  160. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/__init__.py +0 -0
  161. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/cli_fastpath.py +0 -0
  162. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/faces/__init__.py +0 -0
  163. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/faces/cli.py +0 -0
  164. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/faces/http.py +0 -0
  165. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/faces/mibview.py +0 -0
  166. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/faces/nsdp.py +0 -0
  167. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/faces/snmp.py +0 -0
  168. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/seed.py +0 -0
  169. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/server.py +0 -0
  170. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/state.py +0 -0
  171. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web.py +0 -0
  172. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_fastpath_vlan.py +0 -0
  173. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_fastpath_xui.py +0 -0
  174. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_gs105pe.py +0 -0
  175. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_gs110emx.py +0 -0
  176. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_gs110emx_templates.py +0 -0
  177. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_gs728tpp.py +0 -0
  178. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_gsm7228ps.py +0 -0
  179. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_gsm7252ps.py +0 -0
  180. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/src/netgear_switch/virtual/web_m4300.py +0 -0
  181. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/capture_parity.py +0 -0
  182. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_capture.py +0 -0
  183. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_cli_integration.py +0 -0
  184. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_control_poe_port.py +0 -0
  185. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_control_vlan_ip.py +0 -0
  186. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_format.py +0 -0
  187. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_main_framework.py +0 -0
  188. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_new_commands.py +0 -0
  189. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_op_coverage.py +0 -0
  190. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_prompt_wiring.py +0 -0
  191. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_read_commands.py +0 -0
  192. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_resolve.py +0 -0
  193. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_safety.py +0 -0
  194. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/cli/test_serve.py +0 -0
  195. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/conftest.py +0 -0
  196. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/equivalence.py +0 -0
  197. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/captures/gsm7228ps.json +0 -0
  198. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/captures/gsm7252ps.json +0 -0
  199. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/captures/m4300-16x.json +0 -0
  200. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/captures/m4300-24x.json +0 -0
  201. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_environment.txt +0 -0
  202. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_lldp.txt +0 -0
  203. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_mac_table.txt +0 -0
  204. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_network.txt +0 -0
  205. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_poe.txt +0 -0
  206. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_port_all.txt +0 -0
  207. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_show_version.txt +0 -0
  208. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_vlan.txt +0 -0
  209. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_vlan_brief.txt +0 -0
  210. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7228ps_vlan_port_all.txt +0 -0
  211. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_environment.txt +0 -0
  212. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_interface_ethernet_1_0_1.txt +0 -0
  213. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_lldp_remote_device_all.txt +0 -0
  214. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_mac_addr_table.txt +0 -0
  215. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_network.txt +0 -0
  216. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_poe_port_info_all.txt +0 -0
  217. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_port_all.txt +0 -0
  218. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_version.txt +0 -0
  219. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_vlan_90.txt +0 -0
  220. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_vlan_brief.txt +0 -0
  221. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/gsm7252ps_show_vlan_port_all.txt +0 -0
  222. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_environment.txt +0 -0
  223. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_interface_ethernet_1_0_1.txt +0 -0
  224. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_ip_management.txt +0 -0
  225. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_lldp_remote_device_all.txt +0 -0
  226. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_mac_addr_table.txt +0 -0
  227. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_poe_port_info_all.txt +0 -0
  228. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_port_all.txt +0 -0
  229. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_version.txt +0 -0
  230. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_vlan.txt +0 -0
  231. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_vlan_1.txt +0 -0
  232. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_vlan_4.txt +0 -0
  233. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_vlan_5.txt +0 -0
  234. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_16x_show_vlan_port_all.txt +0 -0
  235. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_environment.txt +0 -0
  236. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_interface_ethernet_1_0_1.txt +0 -0
  237. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_ip_management.txt +0 -0
  238. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_lldp_remote_device_all.txt +0 -0
  239. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_mac_addr_table.txt +0 -0
  240. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_port_all.txt +0 -0
  241. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_version.txt +0 -0
  242. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_vlan.txt +0 -0
  243. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_vlan_5.txt +0 -0
  244. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_vlan_90.txt +0 -0
  245. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/cli/m4300_24x_show_vlan_port_all.txt +0 -0
  246. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs105pe_membership.html +0 -0
  247. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs105pe_portstats.html +0 -0
  248. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs105pe_pvid.html +0 -0
  249. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs105pe_status.html +0 -0
  250. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs105pe_switch_info.html +0 -0
  251. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs105pe_vlancfg.html +0 -0
  252. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_cf8021q.html +0 -0
  253. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_interface_stats.html +0 -0
  254. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_login.html +0 -0
  255. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_port_settings.html +0 -0
  256. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_pvid.html +0 -0
  257. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_redirect.html +0 -0
  258. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_sysinfo.html +0 -0
  259. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs110emx_vlanmembership.html +0 -0
  260. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_dashboard.html +0 -0
  261. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_login.html +0 -0
  262. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_membership.html +0 -0
  263. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_poestatus.html +0 -0
  264. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_portstats.html +0 -0
  265. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_pvid.html +0 -0
  266. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs305ep_vlancfg.html +0 -0
  267. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_device_info_and_sensors.xml +0 -0
  268. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_lldp.xml +0 -0
  269. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_macs.xml +0 -0
  270. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_mgmt_ip.xml +0 -0
  271. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_poe.xml +0 -0
  272. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_ports.xml +0 -0
  273. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_pvids_membership.xml +0 -0
  274. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_vlan_membership_per_vlan.xml +0 -0
  275. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gs728tpp_vlans.xml +0 -0
  276. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_basicAddressTable.html +0 -0
  277. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_http_configuration.html +0 -0
  278. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_ipConfiguration.html +0 -0
  279. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_lldpRemoteInventory.html +0 -0
  280. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_poeInterfaceConfiguration.html +0 -0
  281. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_portPvidConfiguration.html +0 -0
  282. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_portStatistics.html +0 -0
  283. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_portsConfiguration.html +0 -0
  284. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_sysInfo.html +0 -0
  285. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_syslog_configuration.html +0 -0
  286. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_vlanPortCfg_vlan5.html +0 -0
  287. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7228ps_vlanStatus.html +0 -0
  288. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_basicAddressTable.html +0 -0
  289. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_http_configuration.html +0 -0
  290. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_https_configuration.html +0 -0
  291. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_ipConfiguration.html +0 -0
  292. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_lldpRemoteInventory.html +0 -0
  293. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_login.html +0 -0
  294. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_poeInterfaceConfiguration.html +0 -0
  295. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_portPvidConfiguration.html +0 -0
  296. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_portStatistics.html +0 -0
  297. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_portsConfiguration.html +0 -0
  298. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_ssh_configuration.html +0 -0
  299. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_sysInfo.html +0 -0
  300. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_syslog_configuration.html +0 -0
  301. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_telnet.html +0 -0
  302. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_user_configuration.html +0 -0
  303. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_user_management.html +0 -0
  304. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan1.html +0 -0
  305. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlanPortCfg_vlan141.html +0 -0
  306. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlanStatus.html +0 -0
  307. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlan_add_form.html +0 -0
  308. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlan_configuration.js +0 -0
  309. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlan_configuration_live.html +0 -0
  310. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/gsm7252ps_vlan_status_live.html +0 -0
  311. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_16x_lldpRemoteInventory.html +0 -0
  312. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_16x_mgmtVlanIpv4Configuration.html +0 -0
  313. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_16x_poeInterfaceConfiguration.html +0 -0
  314. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_16x_portsConfiguration.html +0 -0
  315. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_16x_syslog_configuration.html +0 -0
  316. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_16x_vlanportcfg_vlan4.html +0 -0
  317. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_http_configuration.html +0 -0
  318. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_https_configuration.html +0 -0
  319. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_poeInterfaceConfiguration.html +0 -0
  320. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_ssh_configuration.html +0 -0
  321. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_syslog_configuration.html +0 -0
  322. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_telnet.html +0 -0
  323. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_24x_user_management.html +0 -0
  324. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_addresstable.html +0 -0
  325. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_lldpRemoteInventory.html +0 -0
  326. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_mgmtVlanIpv4Configuration.html +0 -0
  327. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_ports.html +0 -0
  328. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_portstats.html +0 -0
  329. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_pvid.html +0 -0
  330. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_sysinfo.html +0 -0
  331. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_vlanportcfg_vlan1.html +0 -0
  332. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/http/m4300_vlanstatus.html +0 -0
  333. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/snmp/gsm7252ps_fans.txt +0 -0
  334. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/snmp/gsm7252ps_ifhighspeed.txt +0 -0
  335. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/snmp/gsm7252ps_ifoperstatus.txt +0 -0
  336. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/fixtures/snmp/gsm7252ps_psu.txt +0 -0
  337. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/http_specs.py +0 -0
  338. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/cli/test_cli_parse.py +0 -0
  339. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/http/test_crypt.py +0 -0
  340. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/http/test_endpoints.py +0 -0
  341. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/http/test_forms.py +0 -0
  342. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/http/test_goahead_write.py +0 -0
  343. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/http/test_parse.py +0 -0
  344. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/nsdp/test_auth.py +0 -0
  345. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/nsdp/test_parsers.py +0 -0
  346. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/nsdp/test_protocol.py +0 -0
  347. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/nsdp/test_write_frame.py +0 -0
  348. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_client.py +0 -0
  349. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_oids.py +0 -0
  350. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_parse_lldp_macs.py +0 -0
  351. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_parse_mgmt_ip.py +0 -0
  352. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_parse_poe_sensors.py +0 -0
  353. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_parse_ports.py +0 -0
  354. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_parse_system_info.py +0 -0
  355. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_parse_vlans.py +0 -0
  356. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/protocols/snmp/test_write_encode.py +0 -0
  357. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/snmp_fakes.py +0 -0
  358. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_aio_api.py +0 -0
  359. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_capabilities.py +0 -0
  360. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_cli_read.py +0 -0
  361. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_cli_vlan_write.py +0 -0
  362. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_cli_write.py +0 -0
  363. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_config.py +0 -0
  364. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_cross_backend_equivalence.py +0 -0
  365. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_dispatch.py +0 -0
  366. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_docs_coverage.py +0 -0
  367. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_errors.py +0 -0
  368. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_facade_equivalence.py +0 -0
  369. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_flow_control.py +0 -0
  370. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_dispatch.py +0 -0
  371. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_equivalence.py +0 -0
  372. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_hostname_emx.py +0 -0
  373. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_read.py +0 -0
  374. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_services.py +0 -0
  375. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_syslog.py +0 -0
  376. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_users.py +0 -0
  377. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_vlan_membership.py +0 -0
  378. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_write.py +0 -0
  379. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_http_xui_writes.py +0 -0
  380. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_import.py +0 -0
  381. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_models.py +0 -0
  382. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_models_snmp_read.py +0 -0
  383. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_nsdp_integration.py +0 -0
  384. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_nsdp_read.py +0 -0
  385. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_nsdp_write.py +0 -0
  386. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_port_speed.py +0 -0
  387. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_port_speed_http.py +0 -0
  388. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_public_api.py +0 -0
  389. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_quality_gates.py +0 -0
  390. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_registry.py +0 -0
  391. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_snmp_integration.py +0 -0
  392. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_snmp_read.py +0 -0
  393. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_snmp_write.py +0 -0
  394. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_sync_api.py +0 -0
  395. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_syslog_collectors.py +0 -0
  396. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_version.py +0 -0
  397. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/test_write_equivalence.py +0 -0
  398. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/_fastpath_fake.py +0 -0
  399. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_cli_console.py +0 -0
  400. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_cli_scp_cert.py +0 -0
  401. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_cli_telnet.py +0 -0
  402. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_http_client.py +0 -0
  403. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_nsdp_udp_sync.py +0 -0
  404. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_set_transport.py +0 -0
  405. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_snmp_netsnmp_cli.py +0 -0
  406. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_snmp_pysnmp.py +0 -0
  407. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/transport/test_value_parity.py +0 -0
  408. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_cli_cross_backend.py +0 -0
  409. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_cli_gsm7228ps_parity.py +0 -0
  410. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_gs728tpp_vlan_fidelity.py +0 -0
  411. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_gsm7228ps_seed.py +0 -0
  412. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_hostname.py +0 -0
  413. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_m4300_seeds.py +0 -0
  414. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_mibview.py +0 -0
  415. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_mutable_state.py +0 -0
  416. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_nsdp_state.py +0 -0
  417. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_port_description_outcomes.py +0 -0
  418. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_serve_daemon.py +0 -0
  419. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_snmp_write_face.py +0 -0
  420. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_state_seed.py +0 -0
  421. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_switchport_vlan_write.py +0 -0
  422. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_syslog_fidelity.py +0 -0
  423. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_virtual_http_face.py +0 -0
  424. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_virtual_nsdp_face.py +0 -0
  425. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_virtual_snmp_face.py +0 -0
  426. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_web_projection.py +0 -0
  427. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/tests/virtual/test_write_outcomes.py +0 -0
  428. {python_netgear_switch_library-0.0.post405 → python_netgear_switch_library-0.1.post4}/uv.lock +0 -0
@@ -2,7 +2,10 @@ name: CI
2
2
 
3
3
  # Runs the SAME gates as local development (ruff, mypy --strict, pytest with
4
4
  # coverage >= 90) on every push to main and every pull request. A green run here
5
- # is what "mergeable" means; merging to main triggers the release workflows.
5
+ # is what "mergeable" means -- and what "releasable" means: publish-pypi.yml and
6
+ # deb.yml are triggered by this workflow COMPLETING on main and proceed only
7
+ # when it succeeded (workflow_run + conclusion check), so a red push to main
8
+ # is never published. Renaming this workflow breaks that trigger.
6
9
  on:
7
10
  push:
8
11
  branches: [main]
@@ -1,18 +1,30 @@
1
1
  name: Debian Packages + apt repo
2
2
 
3
- # Rolling release: every push to main builds python3-netgear-switch-library for
4
- # Debian trixie AND sid and republishes a signed flat apt repository on GitHub
5
- # Pages (https://mithro.github.io/python-netgear-switch-library/). Mirrors the pattern
6
- # used by other mithro apt-repo projects (sensors2mqtt, ten64-controller). The
7
- # package is Architecture: all (pure Python), so it builds on the default x86
8
- # runner and installs on any Debian arch. No tags: the version is git-derived.
3
+ # Rolling release: every GREEN push to main builds python3-netgear-switch-library
4
+ # for Debian trixie AND sid and republishes a signed flat apt repository on
5
+ # GitHub Pages (https://mith.ro/python-netgear-switch-library/). Mirrors the
6
+ # pattern used by other mithro apt-repo projects (sensors2mqtt, ten64-controller).
7
+ # The package is Architecture: all (pure Python), so it builds on the default x86
8
+ # runner and installs on any Debian arch. The version is git-derived (vX.Y at a
9
+ # tag, vX.Y.postN after it -- identical to the wheel's).
10
+ #
11
+ # Gated on CI exactly like publish-pypi.yml: triggered when the "CI" workflow
12
+ # completes for main, and build-deb only proceeds when that run succeeded, so a
13
+ # red commit never reaches the apt repo. workflow_dispatch stays for re-runs.
9
14
  on:
10
- push:
15
+ workflow_run:
16
+ workflows: [CI]
17
+ types: [completed]
11
18
  branches: [main]
12
19
  workflow_dispatch:
13
20
 
14
21
  jobs:
15
22
  build-deb:
23
+ # workflow_run fires on every completion (failure/cancelled too); only a
24
+ # successful CI run is a release. publish-apt inherits the skip via needs.
25
+ if: >-
26
+ github.event_name == 'workflow_dispatch' ||
27
+ github.event.workflow_run.conclusion == 'success'
16
28
  runs-on: ubuntu-latest
17
29
  strategy:
18
30
  fail-fast: false
@@ -21,7 +33,10 @@ jobs:
21
33
  steps:
22
34
  - uses: actions/checkout@v5
23
35
  with:
24
- fetch-depth: 0 # full history so deb-version.py can count commits
36
+ # Pin to the SHA CI validated, not the default-branch HEAD (which a
37
+ # workflow_run checkout would otherwise use and may already be past).
38
+ ref: ${{ github.event.workflow_run.head_sha || github.sha }}
39
+ fetch-depth: 0 # full history + tags so deb-version.py can describe
25
40
 
26
41
  - name: Build package in debian:${{ matrix.suite }}
27
42
  run: |
@@ -0,0 +1,71 @@
1
+ name: Publish to PyPI
2
+
3
+ # Every GREEN push to main is a release. The version is derived from git by
4
+ # hatch-vcs (see pyproject.toml): vX.Y at a tag, vX.Y.postN N commits later, so
5
+ # each commit produces a new, unique version -- no manual bump.
6
+ #
7
+ # This workflow is gated on CI: it runs when the "CI" workflow (ruff, mypy,
8
+ # pytest + coverage, docs) COMPLETES for a push to main, and the build job only
9
+ # proceeds when that run SUCCEEDED. Before this gate, a red CI run and a PyPI
10
+ # upload raced each other and broken commits were published (13ef042 shipped
11
+ # with the docs job failing, a60bae6 with every test job failing). The
12
+ # workflow_dispatch path stays for manual re-runs.
13
+ #
14
+ # Publishing uses PyPI Trusted Publishing (OIDC): there is no API token in the
15
+ # repo; the `pypi` environment + trusted publisher are registered (see
16
+ # RELEASING.md).
17
+ on:
18
+ workflow_run:
19
+ workflows: [CI]
20
+ types: [completed]
21
+ branches: [main]
22
+ workflow_dispatch:
23
+
24
+ jobs:
25
+ build:
26
+ # workflow_run fires on EVERY completion (failure and cancelled included);
27
+ # only a successful CI run is a release. Dependent jobs inherit the skip.
28
+ if: >-
29
+ github.event_name == 'workflow_dispatch' ||
30
+ github.event.workflow_run.conclusion == 'success'
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - uses: actions/checkout@v5
34
+ with:
35
+ # A workflow_run checkout defaults to the default branch HEAD, which
36
+ # may already be PAST the commit CI just validated. Pin to the SHA
37
+ # CI actually ran on, so the published version is exactly the one
38
+ # that went green.
39
+ ref: ${{ github.event.workflow_run.head_sha || github.sha }}
40
+ fetch-depth: 0 # full history + tags for hatch-vcs version derivation
41
+
42
+ - uses: astral-sh/setup-uv@v7
43
+
44
+ - name: Build sdist + wheel
45
+ run: uv build
46
+
47
+ - name: Show derived version
48
+ run: ls -1 dist/
49
+
50
+ - uses: actions/upload-artifact@v7
51
+ with:
52
+ name: dist
53
+ path: dist/
54
+
55
+ publish:
56
+ needs: build
57
+ runs-on: ubuntu-latest
58
+ # Trusted Publishing requires an OIDC token and a named environment.
59
+ environment: pypi
60
+ permissions:
61
+ id-token: write
62
+ steps:
63
+ - uses: actions/download-artifact@v8
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+
68
+ - name: Publish to PyPI (OIDC trusted publishing)
69
+ uses: pypa/gh-action-pypi-publish@release/v1
70
+ with:
71
+ skip-existing: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: python-netgear-switch-library
3
- Version: 0.0.post405
3
+ Version: 0.1.post4
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
@@ -83,14 +83,14 @@ GitHub Pages apt repository. Pick the line matching your suite:
83
83
 
84
84
  ```sh
85
85
  sudo install -d -m0755 /etc/apt/keyrings
86
- curl -fsSL https://mithro.github.io/python-netgear-switch-library/netgear-switch.gpg \
86
+ curl -fsSL https://mith.ro/python-netgear-switch-library/netgear-switch.gpg \
87
87
  | sudo tee /etc/apt/keyrings/netgear-switch.gpg > /dev/null
88
88
 
89
89
  # trixie:
90
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/trixie/ ./" \
90
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/trixie/ ./" \
91
91
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
92
92
  # sid:
93
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/sid/ ./" \
93
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/sid/ ./" \
94
94
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
95
95
 
96
96
  sudo apt update
@@ -48,14 +48,14 @@ GitHub Pages apt repository. Pick the line matching your suite:
48
48
 
49
49
  ```sh
50
50
  sudo install -d -m0755 /etc/apt/keyrings
51
- curl -fsSL https://mithro.github.io/python-netgear-switch-library/netgear-switch.gpg \
51
+ curl -fsSL https://mith.ro/python-netgear-switch-library/netgear-switch.gpg \
52
52
  | sudo tee /etc/apt/keyrings/netgear-switch.gpg > /dev/null
53
53
 
54
54
  # trixie:
55
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/trixie/ ./" \
55
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/trixie/ ./" \
56
56
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
57
57
  # sid:
58
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/sid/ ./" \
58
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/sid/ ./" \
59
59
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
60
60
 
61
61
  sudo apt update
@@ -1,19 +1,38 @@
1
1
  # Releasing
2
2
 
3
- This project is a **rolling release**. There are no tags and no manual version
4
- bumps: the version is derived from git (`hatch-vcs` for the wheel,
5
- `packaging/deb-version.py` for the `.deb`), and **every merge to `main`
6
- publishes new packages** automatically:
3
+ This project is a **rolling release**. There are no manual version bumps: the
4
+ version is derived from `git describe` (`hatch-vcs` for the wheel,
5
+ `packaging/deb-version.py` for the `.deb`) `X.Y` at a `vX.Y` tag,
6
+ `X.Y.postN` N commits after it — and **every green merge to `main` publishes
7
+ new packages** automatically:
7
8
 
8
9
  - `.github/workflows/ci.yml` — runs the gates (ruff, mypy --strict, pytest with
9
- coverage >= 90) on every push and PR. A green run is what "mergeable" means.
10
+ coverage >= 90, docs build) on every push and PR. A green run is what
11
+ "mergeable" means, and it is what triggers the two release workflows.
10
12
  - `.github/workflows/publish-pypi.yml` — builds and uploads the wheel + sdist to
11
- PyPI on every push to `main`.
13
+ PyPI when CI **succeeds** on `main` (`workflow_run`; a failed or cancelled CI
14
+ run publishes nothing, and the checkout is pinned to the SHA CI validated).
12
15
  - `.github/workflows/deb.yml` — builds `.deb`s for trixie + sid and republishes
13
- the signed apt repo on GitHub Pages on every push to `main`.
16
+ the signed apt repo on GitHub Pages, gated the same way.
14
17
 
15
18
  Merges to `main` MUST use `--no-ff` merge commits so history stays linear per PR.
16
19
 
20
+ ## Tags
21
+
22
+ Tags are the only human input to the version. `v0.0` sits on the root commit
23
+ (so `git describe` works from the start of history, per the repo-setup
24
+ guidance) and `v0.1` marks where the CI-gated pipeline landed (2026-08-22). The
25
+ fallback before any tag existed was `0.0.post<commit count>`, which reached
26
+ `0.0.post405` on PyPI; `v0.1` was chosen because it sorts above that in both
27
+ PEP 440 and Debian ordering, whereas counting from a root tag alone would have
28
+ re-issued `0.0.post404`/`405` (and `skip-existing` would have silently dropped
29
+ the upload).
30
+
31
+ To cut a new series, push an annotated `vX.Y` tag on `main` (a GitHub tag
32
+ ruleset only admits `vXX.ZZZ`-shaped tags) — the next green CI run publishes
33
+ `X.Y`, and every commit after it `X.Y.postN`. Never move or delete a tag that
34
+ has been published from.
35
+
17
36
  ## One-time human setup
18
37
 
19
38
  These steps are done ONCE by a maintainer. **No secret or key is ever committed
@@ -83,7 +102,7 @@ re-runs idempotent.
83
102
 
84
103
  1. Settings → Pages → Source: **GitHub Actions**.
85
104
  2. The `deb.yml` `publish-apt` job deploys the apt repo to
86
- `https://mithro.github.io/python-netgear-switch-library/`. It targets the
105
+ `https://mith.ro/python-netgear-switch-library/`. It targets the
87
106
  `github-pages` deployment environment, which GitHub creates automatically
88
107
  once Pages is enabled — no separate environment setup is needed for this
89
108
  one (unlike the `pypi` environment above, which must be created by hand).
@@ -91,7 +110,8 @@ re-runs idempotent.
91
110
  ## Verifying a release
92
111
 
93
112
  - PyPI: check https://pypi.org/project/python-netgear-switch-library/ for the new
94
- `0.0.postN` version.
113
+ `X.Y.postN` version (matches `git describe --tags` on `main`, e.g. `v0.1-3-g…`
114
+ → `0.1.post3`).
95
115
  - apt: `sudo apt update && apt-cache policy python3-netgear-switch-library` on a
96
116
  Debian trixie/sid box configured per the README. Before the GPG key is set
97
117
  (see step 2 above), expect `apt update` to fail with a signature error for
@@ -20,8 +20,11 @@ Architecture: all
20
20
  Depends: ${python3:Depends},
21
21
  ${misc:Depends},
22
22
  snmp
23
- Recommends: python3-httpx
24
- Suggests: python3-pysnmp
23
+ Recommends: python3-httpx,
24
+ python3-mcp
25
+ Suggests: python3-pysnmp,
26
+ python3-paramiko,
27
+ python3-serial
25
28
  Description: query and control Netgear switches over SNMP, NSDP and HTTP
26
29
  Python library (import name netgear_switch) and CLI (ngsw) to query and control
27
30
  Netgear managed and Plus switches behind one model-driven API. Managed switches
@@ -30,4 +33,6 @@ Description: query and control Netgear switches over SNMP, NSDP and HTTP
30
33
  The synchronous SNMP transport shells out to the net-snmp command-line tools
31
34
  (snmpget/snmpbulkwalk/snmpset) from the "snmp" package, which is why it is a
32
35
  hard runtime dependency. The optional asynchronous transport uses python3-pysnmp
33
- and the HTTP transport uses python3-httpx.
36
+ and the HTTP transport uses python3-httpx. The ngsw-mcp MCP server (installed
37
+ unconditionally) needs python3-mcp; the FASTPATH SSH/serial CLI backends need
38
+ python3-paramiko / python3-serial.
@@ -82,14 +82,14 @@ GitHub Pages apt repository:
82
82
  .. code-block:: sh
83
83
 
84
84
  sudo install -d -m0755 /etc/apt/keyrings
85
- curl -fsSL https://mithro.github.io/python-netgear-switch-library/netgear-switch.gpg \
85
+ curl -fsSL https://mith.ro/python-netgear-switch-library/netgear-switch.gpg \
86
86
  | sudo tee /etc/apt/keyrings/netgear-switch.gpg > /dev/null
87
87
 
88
88
  # trixie:
89
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/trixie/ ./" \
89
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/trixie/ ./" \
90
90
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
91
91
  # sid:
92
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/sid/ ./" \
92
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/sid/ ./" \
93
93
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
94
94
 
95
95
  sudo apt update
@@ -102,10 +102,11 @@ the net-snmp tools automatically. The packaging lives in ``debian/`` and
102
102
  Versioning
103
103
  ----------
104
104
 
105
- A **rolling release**. The version is derived from git by ``hatch-vcs``
106
- (``0.0.postN``, or ``X.Y.postN`` once a ``vX.Y`` tag exists); every merge to
107
- ``main`` publishes to PyPI and to the apt repository. No manual version bumps —
108
- see ``RELEASING.md``.
105
+ A **rolling release**. The version is derived from ``git describe`` by
106
+ ``hatch-vcs``: ``X.Y`` at a ``vX.Y`` tag, ``X.Y.postN`` N commits after it
107
+ (``0.1``, ``0.1.post1``, …). Every merge to ``main`` whose CI run is green
108
+ publishes to PyPI and to the apt repository — a red run publishes nothing. No
109
+ manual version bumps — see ``RELEASING.md``.
109
110
 
110
111
  .. note::
111
112
 
@@ -2,17 +2,32 @@ MCP server
2
2
  ==========
3
3
 
4
4
  ``ngsw-mcp`` exposes the library as `Model Context Protocol
5
- <https://modelcontextprotocol.io>`_ tools over stdio, so an LLM-driven client
6
- can inspect — and, if you let it, reconfigure — your switches.
5
+ <https://modelcontextprotocol.io>`_ tools, so an LLM-driven client can inspect
6
+ — and, if you let it, reconfigure — your switches.
7
7
 
8
8
  .. code-block:: sh
9
9
 
10
10
  pip install 'python-netgear-switch-library[mcp]'
11
- ngsw-mcp
11
+ ngsw-mcp # stdio: one client, on stdin/stdout
12
+ ngsw-mcp --transport streamable-http --host 127.0.0.1 --port 8766 # shared
13
+
14
+ Transports
15
+ ----------
16
+
17
+ ``--transport stdio`` (the default) serves the one client that spawned the
18
+ process. ``--transport streamable-http`` listens on ``--host``/``--port`` so any
19
+ number of clients share one long-lived server — for example a systemd
20
+ socket-activated service that several agents on the same host connect to.
21
+ Each option also has an environment form, ``NGSW_MCP_TRANSPORT``,
22
+ ``NGSW_MCP_HOST`` and ``NGSW_MCP_PORT`` (a flag beats the environment), so a
23
+ service unit can set them once and keep ``ExecStart=ngsw-mcp``. An invalid
24
+ transport or port — from either tier — is rejected at start-up.
12
25
 
13
26
  Configuring a client
14
27
  --------------------
15
28
 
29
+ A client that spawns its own server (stdio):
30
+
16
31
  .. code-block:: json
17
32
 
18
33
  {
@@ -32,6 +47,16 @@ Every tool resolves its target switch through the **same resolver the CLI uses**
32
47
  with credentials layered from arguments, environment and inventory. See
33
48
  :doc:`guide/configuration`.
34
49
 
50
+ A client connecting to a shared streamable-http server instead names its URL:
51
+
52
+ .. code-block:: json
53
+
54
+ {
55
+ "mcpServers": {
56
+ "netgear": {"type": "http", "url": "http://127.0.0.1:8765/mcp"}
57
+ }
58
+ }
59
+
35
60
  .. note::
36
61
 
37
62
  ``$NGSW_INVENTORY`` is an MCP-server convenience — an MCP client has no
@@ -14,10 +14,10 @@ Rolling release &mdash; every push to <code>main</code> publishes a new version
14
14
  <h2>Setup (Debian trixie)</h2>
15
15
  <pre>
16
16
  sudo install -d -m0755 /etc/apt/keyrings
17
- curl -fsSL https://mithro.github.io/python-netgear-switch-library/netgear-switch.gpg \
17
+ curl -fsSL https://mith.ro/python-netgear-switch-library/netgear-switch.gpg \
18
18
  | sudo tee /etc/apt/keyrings/netgear-switch.gpg > /dev/null
19
19
 
20
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/trixie/ ./" \
20
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/trixie/ ./" \
21
21
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
22
22
 
23
23
  sudo apt update
@@ -27,10 +27,10 @@ sudo apt install python3-netgear-switch-library
27
27
  <h2>Setup (Debian sid)</h2>
28
28
  <pre>
29
29
  sudo install -d -m0755 /etc/apt/keyrings
30
- curl -fsSL https://mithro.github.io/python-netgear-switch-library/netgear-switch.gpg \
30
+ curl -fsSL https://mith.ro/python-netgear-switch-library/netgear-switch.gpg \
31
31
  | sudo tee /etc/apt/keyrings/netgear-switch.gpg > /dev/null
32
32
 
33
- echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mithro.github.io/python-netgear-switch-library/sid/ ./" \
33
+ echo "deb [signed-by=/etc/apt/keyrings/netgear-switch.gpg] https://mith.ro/python-netgear-switch-library/sid/ ./" \
34
34
  | sudo tee /etc/apt/sources.list.d/netgear-switch.list
35
35
 
36
36
  sudo apt update
@@ -63,11 +63,16 @@ ngsw-mcp = "netgear_switch.mcp.server:main"
63
63
  [dependency-groups]
64
64
  dev = ["pytest>=8.0", "ruff>=0.6", "mypy>=1.11", "pytest-cov>=5.0"]
65
65
 
66
- # Rolling release: the version is derived from git, never hand-edited and never
67
- # tagged. Every commit on main increments the commit height, so post-release
68
- # versioning yields a real, monotonic version (0.0.postN with no tag; X.Y.postN
69
- # once a vX.Y tag ever exists). "no-local-version" keeps the version PyPI-upload
70
- # safe. These forms are also valid Debian versions verbatim.
66
+ # Rolling release: the version is derived from git (`git describe`), never
67
+ # hand-edited. vX.Y tags (v0.0 on the root commit per the repo-setup guidance,
68
+ # v0.1 when the CI-gated release pipeline landed on 2026-08-22) are the only
69
+ # human input; every commit after a tag increments the distance, so post-release
70
+ # versioning yields a real, monotonic version: X.Y at the tag, X.Y.postN N
71
+ # commits later. (Before any tag existed the fallback was 0.0.post<commit
72
+ # count>, which reached 0.0.post405 on PyPI -- v0.1 was chosen to sort ABOVE
73
+ # that, since re-counting from a root tag would have re-issued 0.0.post404/405.)
74
+ # "no-local-version" keeps the version PyPI-upload safe. These forms are also
75
+ # valid Debian versions verbatim; packaging/deb-version.py applies the same rule.
71
76
  [tool.hatch.version]
72
77
  source = "vcs"
73
78
 
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.0.post405'
22
- __version_tuple__ = version_tuple = (0, 0, 'post405')
21
+ __version__ = version = '0.1.post4'
22
+ __version_tuple__ = version_tuple = (0, 1, 'post4')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -37,6 +37,8 @@ from ..models import PortSpeed, VlanMode
37
37
  from ..registry import Backend
38
38
 
39
39
  if TYPE_CHECKING:
40
+ from collections.abc import Mapping, Sequence
41
+
40
42
  from ..sync_api import SyncSwitch
41
43
 
42
44
  _WRITE_ENV = "NGSW_MCP_ALLOW_WRITES"
@@ -168,13 +170,28 @@ def list_inventory_switches(
168
170
  ]
169
171
 
170
172
 
171
- def build_server(env: dict[str, str] | None = None): # type: ignore[no-untyped-def]
173
+ def build_server( # type: ignore[no-untyped-def]
174
+ env: dict[str, str] | None = None,
175
+ *,
176
+ host: str | None = None,
177
+ port: int | None = None,
178
+ ):
172
179
  """Construct the FastMCP server. Write tools are registered only when
173
- ``$NGSW_MCP_ALLOW_WRITES`` is set (see module docstring)."""
180
+ ``$NGSW_MCP_ALLOW_WRITES`` is set (see module docstring).
181
+
182
+ ``host``/``port`` are the bind address for the HTTP transport (see
183
+ :func:`main`); they are passed to FastMCP only when given, so the stdio
184
+ default is untouched.
185
+ """
174
186
  from mcp.server.fastmcp import FastMCP
175
187
 
176
188
  env = env if env is not None else dict(os.environ)
177
- mcp = FastMCP("netgear-switch")
189
+ bind: dict[str, Any] = {}
190
+ if host is not None:
191
+ bind["host"] = host
192
+ if port is not None:
193
+ bind["port"] = port
194
+ mcp = FastMCP("netgear-switch", **bind)
178
195
 
179
196
  def resolver(
180
197
  switch: str | None,
@@ -811,9 +828,71 @@ def _register_write_tools(mcp, resolver) -> None: # type: ignore[no-untyped-def
811
828
  )
812
829
 
813
830
 
814
- def main() -> None:
815
- """Entry point (``ngsw-mcp``): run the server over stdio."""
816
- build_server().run()
831
+ _TRANSPORTS = ("stdio", "streamable-http")
832
+ _TRANSPORT_ENV = "NGSW_MCP_TRANSPORT"
833
+ _HOST_ENV = "NGSW_MCP_HOST"
834
+ _PORT_ENV = "NGSW_MCP_PORT"
835
+
836
+
837
+ def _parse_args(
838
+ argv: Sequence[str] | None, env: Mapping[str, str]
839
+ ) -> argparse.Namespace:
840
+ """``ngsw-mcp`` options. Flag > ``$NGSW_MCP_*`` environment > default.
841
+
842
+ The environment tier exists for service managers: a systemd unit sets
843
+ ``Environment=NGSW_MCP_TRANSPORT=streamable-http`` once and the ExecStart
844
+ stays a bare ``ngsw-mcp``. Defaults are strings so argparse applies the
845
+ same ``choices``/``type`` validation to an env value as to a flag -- a
846
+ bad ``$NGSW_MCP_PORT`` is rejected up front, not at bind time.
847
+ """
848
+ ap = argparse.ArgumentParser(
849
+ prog="ngsw-mcp",
850
+ description=(
851
+ "MCP server over the python-netgear-switch-library read/write API. "
852
+ "stdio (default) serves one client on stdin/stdout; streamable-http "
853
+ "listens on --host:--port for any number of clients."
854
+ ),
855
+ )
856
+ ap.add_argument(
857
+ "--transport",
858
+ choices=_TRANSPORTS,
859
+ default=env.get(_TRANSPORT_ENV, "stdio"),
860
+ help=f"MCP transport (default: ${_TRANSPORT_ENV} or stdio)",
861
+ )
862
+ ap.add_argument(
863
+ "--host",
864
+ default=env.get(_HOST_ENV),
865
+ help=f"bind address for streamable-http (default: ${_HOST_ENV}, "
866
+ "else FastMCP's 127.0.0.1)",
867
+ )
868
+ ap.add_argument(
869
+ "--port",
870
+ type=int,
871
+ default=env.get(_PORT_ENV),
872
+ help=f"TCP port for streamable-http (default: ${_PORT_ENV}, "
873
+ "else FastMCP's 8000)",
874
+ )
875
+ args = ap.parse_args(argv)
876
+ # argparse validates `choices` only for values given on the command line,
877
+ # not for a default -- so an env-supplied transport is checked by hand.
878
+ if args.transport not in _TRANSPORTS:
879
+ ap.error(
880
+ f"argument --transport (from ${_TRANSPORT_ENV}): invalid choice: "
881
+ f"{args.transport!r} (choose from {', '.join(_TRANSPORTS)})"
882
+ )
883
+ return args
884
+
885
+
886
+ def main(
887
+ argv: Sequence[str] | None = None, env: Mapping[str, str] | None = None
888
+ ) -> None:
889
+ """Entry point (``ngsw-mcp``): build the server and run it over the chosen
890
+ transport -- stdio by default, or streamable-http on ``--host``/``--port``
891
+ for a shared, long-lived server (e.g. systemd socket activation)."""
892
+ env = env if env is not None else dict(os.environ)
893
+ args = _parse_args(argv, env)
894
+ server = build_server(dict(env), host=args.host, port=args.port)
895
+ server.run(transport=args.transport)
817
896
 
818
897
 
819
898
  if __name__ == "__main__":
@@ -6,11 +6,14 @@ container with no third-party packages installed), so these tests drive it
6
6
  via ``subprocess`` under plain ``python3`` rather than importing it -- that
7
7
  also proves it truly needs nothing beyond the standard library.
8
8
 
9
- The commit count grows every time this repo gets a new commit, so nothing
10
- here hard-codes today's ``post<N>`` value; each assertion recomputes the
11
- expected value from ``git`` at test time, mirroring the derivation logic
12
- itself (see the brief's own verification: ``0.0.post$(git rev-list --count
13
- HEAD)``).
9
+ The version changes every time this repo gets a commit or a tag, so nothing
10
+ here hard-codes today's value; each assertion recomputes the expected value
11
+ from ``git`` at test time. The live-repo check deliberately uses DIFFERENT git
12
+ commands from the script (``describe --abbrev=0`` + ``rev-list --count
13
+ TAG..HEAD`` versus the script's ``describe --long`` regex) so the two can
14
+ disagree if either is wrong. The tagless fallback (``0.0.post<count>``) is
15
+ exercised in a throwaway repo: the live repo has been tagged since v0.0/v0.1
16
+ on 2026-08-22, so it no longer takes that path.
14
17
  """
15
18
 
16
19
  from __future__ import annotations
@@ -116,12 +119,37 @@ def test_only_stdlib_imports():
116
119
  assert seen <= stdlib_allow, f"non-stdlib imports found: {seen - stdlib_allow}"
117
120
 
118
121
 
119
- def test_version_matches_commit_count_fallback():
120
- """No tags exist upstream, so the fallback path (0.0.post<count>) is live."""
121
- expected = f"0.0.post{_git('rev-list', '--count', 'HEAD')}"
122
+ def test_live_repo_version_matches_git_describe():
123
+ """The real checkout: X.Y at the nearest vX.Y tag, X.Y.postN N commits on.
124
+
125
+ Expected value is derived independently of the script: the nearest tag via
126
+ ``describe --abbrev=0`` and the distance via ``rev-list --count TAG..HEAD``
127
+ (the script parses ``describe --long`` instead). This broke on 2026-08-22
128
+ when v0.1 was pushed and the old assertion still expected the tagless
129
+ ``0.0.post<count>`` fallback -- CI went red and, correctly, nothing was
130
+ published for that commit.
131
+ """
132
+ tag = _git("describe", "--tags", "--abbrev=0", "--match", "v[0-9]*")
133
+ assert tag.startswith("v"), tag
134
+ base = tag[1:]
135
+ distance = int(_git("rev-list", "--count", f"{tag}..HEAD"))
136
+ expected = base if distance == 0 else f"{base}.post{distance}"
122
137
  assert _run() == expected
123
138
 
124
139
 
140
+ def test_commit_count_fallback_in_tagless_repo(tmp_path):
141
+ """With no vX.Y tag at all the version is 0.0.post<commit count>.
142
+
143
+ The live repo left this path when it was tagged, so exercise it in a
144
+ throwaway repo: three commits, no tags -> 0.0.post3.
145
+ """
146
+ repo = tmp_path / "tagless-repo"
147
+ _init_repo(repo)
148
+ for i in range(3):
149
+ _commit(repo, f"commit {i}")
150
+ assert _run_in_repo(repo).stdout.strip() == "0.0.post3"
151
+
152
+
125
153
  def test_version_is_a_valid_debian_version_string():
126
154
  printed = _run()
127
155
  assert _DEBIAN_VERSION_RE.match(printed), printed
@@ -159,9 +187,10 @@ def test_write_changelog_produces_parseable_debian_changelog(tmp_path):
159
187
  def test_version_tag_based_path(tmp_path):
160
188
  """vX.Y -> X.Y at the tag; X.Y.postN with N commits after it.
161
189
 
162
- The live upstream repo has no tags, so this path (the ``git describe``
163
- branch of ``version()``) has no coverage from the other tests above --
164
- exercise it directly against a throwaway repo instead.
190
+ This is the path the live upstream repo takes since v0.0 (root commit) and
191
+ v0.1 (2026-08-22) were tagged; the live-repo test above checks it against
192
+ the real checkout, this one pins the exact arithmetic on a throwaway
193
+ repo whose tag and distance are fully controlled.
165
194
  """
166
195
  repo = tmp_path / "tag-repo"
167
196
  _init_repo(repo)