diff --git a/changelog/69185.added.md b/changelog/69185.added.md new file mode 100644 index 000000000000..67bb195a6076 --- /dev/null +++ b/changelog/69185.added.md @@ -0,0 +1 @@ +Migrate Salt documentation to the PyData Sphinx theme. This update modernizes the documentation UI, improves navigation with a persistent sidebar tree, and fixes issues with embedded video playback. diff --git a/doc/_static/versions.json b/doc/_static/versions.json new file mode 100644 index 000000000000..23807ac18947 --- /dev/null +++ b/doc/_static/versions.json @@ -0,0 +1,17 @@ +[ + { + "name": "Latest", + "version": "latest", + "url": "https://docs.saltproject.io/en/latest/" + }, + { + "name": "3006.24", + "version": "3006.24", + "url": "https://docs.saltproject.io/en/3006.24/" + }, + { + "name": "Master (Dev)", + "version": "master", + "url": "https://docs.saltproject.io/en/master/" + } +] diff --git a/doc/_templates/header-links.html b/doc/_templates/header-links.html new file mode 100644 index 000000000000..783074febfad --- /dev/null +++ b/doc/_templates/header-links.html @@ -0,0 +1,76 @@ + + + + diff --git a/doc/conf.py b/doc/conf.py index 54f461db52a1..cc8f92693897 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -2,11 +2,94 @@ """ Sphinx documentation for Salt """ +import logging import os +import sys +import urllib.parse + +# Fix urllib.parse.urlsplit/urlparse bug (Invalid IPv6 URL) +# This bug causes Sphinx to crash when encountering documentation examples +# like http://hostname[:port] which are incorrectly parsed as malformed IPv6 URLs. +# We patch it globally to ensure all extensions (like pydata-sphinx-theme) are safe. +_original_urlsplit = urllib.parse.urlsplit +_original_urlparse = urllib.parse.urlparse +log = logging.getLogger("salt.docs.conf") + + +def _safe_urlsplit(url, scheme="", allow_fragments=True): + try: + return _original_urlsplit(url, scheme, allow_fragments) + except Exception: + # Return a safe dummy SplitResult for ANY error + return urllib.parse.SplitResult(scheme, "invalid-url", str(url), "", "") + + +def _safe_urlparse(url, scheme="", allow_fragments=True): + try: + return _original_urlparse(url, scheme, allow_fragments) + except Exception: + # Return a safe dummy ParseResult for ANY error + return urllib.parse.ParseResult(scheme, "invalid-url", str(url), "", "", "") + + +urllib.parse.urlsplit = _safe_urlsplit +urllib.parse.urlparse = _safe_urlparse + +# Ensure any modules that already imported urlsplit or urlparse also use the patched versions. +for mod in list(sys.modules.values()): + if mod: + if hasattr(mod, "urlsplit") and mod.urlsplit is _original_urlsplit: + mod.urlsplit = _safe_urlsplit + if hasattr(mod, "urlparse") and mod.urlparse is _original_urlparse: + mod.urlparse = _safe_urlparse + +# Force disable pydata_sphinx_theme link shortener as it's prone to crashing on Salt docs +try: + import pydata_sphinx_theme.short_link + + def _no_op_run(self, **kwargs): + pass + + pydata_sphinx_theme.short_link.ShortenLinkTransform.run = _no_op_run +except ImportError: + pass + + +# Patch urllib3 if it's available, as it has its own unsafe URL parsing +def _patch_urllib3(module): + try: + _original_parse_url = module.util.url.parse_url + + def _safe_parse_url(url): + try: + return _original_parse_url(url) + except (ValueError, AttributeError): + log.warning("urllib3 detected malformed URL: %s", url) + # Return a safe dummy Url object + return module.util.url.Url(path=url) + + module.util.url.parse_url = _safe_parse_url + except (ImportError, AttributeError): + pass + + +try: + import urllib3 + + _patch_urllib3(urllib3) +except ImportError: + pass + +try: + import requests.packages.urllib3 as requests_urllib3 + + _patch_urllib3(requests_urllib3) +except (ImportError, AttributeError): + pass + import pathlib import re import shutil -import sys import textwrap import time import types @@ -178,7 +261,7 @@ # Smart dependency handling for documentation builds # For man pages (lightweight CLI docs), we auto-mock missing dependencies # For full HTML docs, we fail with helpful errors about what's missing -autodoc_mock_imports = [] +autodoc_mock_imports = ["cgi"] # Detect if we're building man pages only (lightweight build) # Man pages only document CLI tools and don't need full Salt imports @@ -213,11 +296,11 @@ ", ".join(_missing_deps), ) else: - # For HTML/full builds, warn that docs may be incomplete - log.warning( + # For HTML/full builds, log info that docs may be incomplete + log.info( "\n" "=" * 70 + "\n" - "WARNING: Missing dependencies for full documentation build:\n" + "INFO: Missing optional dependencies for full documentation build:\n" " %s\n\n" "Autodoc will use mocked modules. Documentation will be generated but\n" "may be incomplete or show incorrect type hints.\n\n" @@ -264,7 +347,7 @@ ### HTML options # set 'HTML_THEME=saltstack' to use previous theme -html_theme = os.environ.get("HTML_THEME", "saltstack2") +html_theme = os.environ.get("HTML_THEME", "pydata_sphinx_theme") html_theme_path = ["_themes"] html_title = "" html_short_title = "Salt" @@ -292,18 +375,47 @@ "sourcelink.html", "saltstack.html", ] -html_sidebars = { - "ref/**/all/salt.*": [ - html_search_template, - "version.html", - "modules-sidebar.html", - "localtoc.html", - "relations.html", - "sourcelink.html", - "saltstack.html", - ], - "ref/formula/all/*": [], -} +if html_theme == "pydata_sphinx_theme": + html_theme_options = { + "logo": { + "image_light": "https://gitlab.com/saltstack/open/salt-branding-guide/-/raw/master/logos/SaltProject_altlogo_teal.png", + "image_dark": "https://gitlab.com/saltstack/open/salt-branding-guide/-/raw/master/logos/SaltProject_altlogo_teal.png", + }, + "navbar_start": ["navbar-logo"], + "navbar_center": [ + "navbar-nav", + "header-links", + ], # navbar-nav provides structure, header-links provides logic + "navbar_end": ["version-switcher", "theme-switcher", "navbar-icon-links"], + "show_nav_level": 4, + "navigation_depth": 4, + "collapse_navigation": False, + "shorten_urls": False, # Disable to avoid crashes on malformed URLs + "check_switcher": False, # Disable to avoid warnings about local json file + "switcher": { + "json_url": "https://docs.saltproject.io/en/latest/_static/versions.json", + "version_match": "master", + }, + } + html_sidebars = {"**": ["globaltoc.html", "sidebar-ethical-ads"]} + + +elif html_theme == "furo": + pass + +else: + html_sidebars = { + "ref/**/all/salt.*": [ + html_search_template, + "version.html", + "modules-sidebar.html", + "localtoc.html", + "relations.html", + "sourcelink.html", + "saltstack.html", + ], + "ref/formula/all/*": [], + } html_context = { "on_saltstack": on_saltstack, @@ -443,11 +555,15 @@ class ReleasesTree(TocTree): option_spec = dict(TocTree.option_spec) def run(self): - rst = super().run() - entries = rst[0][0]["entries"][:] - entries.sort(key=_normalize_version, reverse=True) - rst[0][0]["entries"][:] = entries - return rst + try: + rst = super().run() + if rst and rst[0] and rst[0][0] and "entries" in rst[0][0]: + entries = rst[0][0]["entries"][:] + entries.sort(key=_normalize_version, reverse=True) + rst[0][0]["entries"][:] = entries + return rst + except (AttributeError, KeyError, IndexError, TypeError): + return super().run() def copy_release_templates_pre(app): diff --git a/doc/topics/cloud/index.html b/doc/topics/cloud/index.html index 5a05baa217f1..8a6fe5f69a4a 100644 --- a/doc/topics/cloud/index.html +++ b/doc/topics/cloud/index.html @@ -6,7 +6,7 @@
- +
diff --git a/doc/topics/ssh/index.html b/doc/topics/ssh/index.html index 37009d219692..e2fd1c5d3ccd 100644 --- a/doc/topics/ssh/index.html +++ b/doc/topics/ssh/index.html @@ -6,7 +6,7 @@
- +
@@ -20,13 +20,13 @@
- +
- +
diff --git a/requirements/static/ci/docs.in b/requirements/static/ci/docs.in index 6a76caac9566..489f156f588e 100644 --- a/requirements/static/ci/docs.in +++ b/requirements/static/ci/docs.in @@ -5,4 +5,5 @@ sphinxcontrib-httpdomain>=1.8.0 sphinxcontrib-spelling cherrypy jinja2 +pydata-sphinx-theme MarkupSafe<3.0.0 diff --git a/requirements/static/ci/py3.10/docs.txt b/requirements/static/ci/py3.10/docs.txt index 0ba35ac8a73b..20f40de61966 100644 --- a/requirements/static/ci/py3.10/docs.txt +++ b/requirements/static/ci/py3.10/docs.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/base.txt requirements/zeromq.txt requirements/crypto.txt requirements/static/ci/docs.in --python-platform=linux --python-version=3.10 --constraint requirements/constraints.txt --no-emit-index-url --unsafe-package=setuptools -c=requirements/static/ci/py3.10/linux.txt -o=requirements/static/ci/py3.10/docs.txt +accessible-pygments==0.0.5 + # via pydata-sphinx-theme aiohappyeyeballs==2.6.1 # via # -c requirements/static/ci/py3.10/linux.txt @@ -31,11 +33,15 @@ autocommand==2.2.2 # -c requirements/static/ci/py3.10/linux.txt # jaraco-text babel==2.12.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx backports-tarfile==1.2.0 # via # -c requirements/static/ci/py3.10/linux.txt # jaraco-context +beautifulsoup4==4.14.3 + # via pydata-sphinx-theme certifi==2024.7.4 # via # -c requirements/static/ci/py3.10/linux.txt @@ -82,7 +88,9 @@ distro==1.8.0 # -c requirements/static/ci/py3.10/linux.txt # -r requirements/base.txt docutils==0.20.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx filelock==3.20.3 # via # -c requirements/static/ci/py3.10/linux.txt @@ -225,10 +233,15 @@ pycryptodomex==3.23.0 # -c requirements/static/ci/py3.10/linux.txt # -r requirements/base.txt # -r requirements/crypto.txt +pydata-sphinx-theme==0.17.1 + # via -r requirements/static/ci/docs.in pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.17.2 - # via sphinx + # via + # accessible-pygments + # pydata-sphinx-theme + # sphinx pyopenssl==26.0.0 # via # -c requirements/static/ci/py3.10/linux.txt @@ -282,9 +295,12 @@ smmap==5.0.2 # gitdb snowballstemmer==2.2.0 # via sphinx +soupsieve==2.8.3 + # via beautifulsoup4 sphinx==7.0.1 # via # -r requirements/static/ci/docs.in + # pydata-sphinx-theme # sphinxcontrib-httpdomain # sphinxcontrib-spelling sphinxcontrib-applehelp==1.0.4 @@ -311,7 +327,9 @@ typing-extensions==4.14.1 # via # -c requirements/static/ci/py3.10/linux.txt # aiosignal + # beautifulsoup4 # cryptography + # pydata-sphinx-theme # pyopenssl # virtualenv uc-micro-py==1.0.2 diff --git a/requirements/static/ci/py3.11/docs.txt b/requirements/static/ci/py3.11/docs.txt index 206c4830bacd..f9db5cbed604 100644 --- a/requirements/static/ci/py3.11/docs.txt +++ b/requirements/static/ci/py3.11/docs.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/base.txt requirements/zeromq.txt requirements/crypto.txt requirements/static/ci/docs.in --python-platform=linux --python-version=3.11 --constraint requirements/constraints.txt --no-emit-index-url --unsafe-package=setuptools -c=requirements/static/ci/py3.11/linux.txt -o=requirements/static/ci/py3.11/docs.txt +accessible-pygments==0.0.5 + # via pydata-sphinx-theme aiohappyeyeballs==2.6.1 # via # -c requirements/static/ci/py3.11/linux.txt @@ -27,11 +29,15 @@ autocommand==2.2.2 # -c requirements/static/ci/py3.11/linux.txt # jaraco-text babel==2.12.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx backports-tarfile==1.2.0 # via # -c requirements/static/ci/py3.11/linux.txt # jaraco-context +beautifulsoup4==4.14.3 + # via pydata-sphinx-theme certifi==2024.7.4 # via # -c requirements/static/ci/py3.11/linux.txt @@ -78,7 +84,9 @@ distro==1.8.0 # -c requirements/static/ci/py3.11/linux.txt # -r requirements/base.txt docutils==0.20.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx filelock==3.20.3 # via # -c requirements/static/ci/py3.11/linux.txt @@ -221,10 +229,15 @@ pycryptodomex==3.23.0 # -c requirements/static/ci/py3.11/linux.txt # -r requirements/base.txt # -r requirements/crypto.txt +pydata-sphinx-theme==0.17.1 + # via -r requirements/static/ci/docs.in pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.19.2 - # via sphinx + # via + # accessible-pygments + # pydata-sphinx-theme + # sphinx pyopenssl==26.0.0 # via # -c requirements/static/ci/py3.11/linux.txt @@ -278,9 +291,12 @@ smmap==5.0.2 # gitdb snowballstemmer==2.2.0 # via sphinx +soupsieve==2.8.3 + # via beautifulsoup4 sphinx==7.0.1 # via # -r requirements/static/ci/docs.in + # pydata-sphinx-theme # sphinxcontrib-httpdomain # sphinxcontrib-spelling sphinxcontrib-applehelp==1.0.4 @@ -307,6 +323,8 @@ typing-extensions==4.14.1 # via # -c requirements/static/ci/py3.11/linux.txt # aiosignal + # beautifulsoup4 + # pydata-sphinx-theme # pyopenssl uc-micro-py==1.0.1 # via linkify-it-py diff --git a/requirements/static/ci/py3.12/docs.txt b/requirements/static/ci/py3.12/docs.txt index 66a86020dc29..1fde450977f5 100644 --- a/requirements/static/ci/py3.12/docs.txt +++ b/requirements/static/ci/py3.12/docs.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/base.txt requirements/zeromq.txt requirements/crypto.txt requirements/static/ci/docs.in --python-platform=linux --python-version=3.12 --constraint requirements/constraints.txt --no-emit-index-url --unsafe-package=setuptools -c=requirements/static/ci/py3.12/linux.txt -o=requirements/static/ci/py3.12/docs.txt +accessible-pygments==0.0.5 + # via pydata-sphinx-theme aiohappyeyeballs==2.6.1 # via # -c requirements/static/ci/py3.12/linux.txt @@ -27,7 +29,11 @@ autocommand==2.2.2 # -c requirements/static/ci/py3.12/linux.txt # jaraco-text babel==2.12.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx +beautifulsoup4==4.14.3 + # via pydata-sphinx-theme certifi==2024.7.4 # via # -c requirements/static/ci/py3.12/linux.txt @@ -74,7 +80,9 @@ distro==1.8.0 # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt docutils==0.20.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx filelock==3.20.3 # via # -c requirements/static/ci/py3.12/linux.txt @@ -217,10 +225,15 @@ pycryptodomex==3.23.0 # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt # -r requirements/crypto.txt +pydata-sphinx-theme==0.17.1 + # via -r requirements/static/ci/docs.in pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.19.2 - # via sphinx + # via + # accessible-pygments + # pydata-sphinx-theme + # sphinx pyopenssl==26.0.0 # via # -c requirements/static/ci/py3.12/linux.txt @@ -274,9 +287,12 @@ smmap==5.0.2 # gitdb snowballstemmer==2.2.0 # via sphinx +soupsieve==2.8.3 + # via beautifulsoup4 sphinx==7.0.1 # via # -r requirements/static/ci/docs.in + # pydata-sphinx-theme # sphinxcontrib-httpdomain # sphinxcontrib-spelling sphinxcontrib-applehelp==1.0.4 @@ -303,6 +319,8 @@ typing-extensions==4.14.1 # via # -c requirements/static/ci/py3.12/linux.txt # aiosignal + # beautifulsoup4 + # pydata-sphinx-theme # pyopenssl uc-micro-py==1.0.1 # via linkify-it-py diff --git a/requirements/static/ci/py3.13/docs.txt b/requirements/static/ci/py3.13/docs.txt index 2f36be7a553b..8f0a113139ae 100644 --- a/requirements/static/ci/py3.13/docs.txt +++ b/requirements/static/ci/py3.13/docs.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/base.txt requirements/zeromq.txt requirements/crypto.txt requirements/static/ci/docs.in --python-platform=linux --python-version=3.13 --constraint requirements/constraints.txt --no-emit-index-url --unsafe-package=setuptools -c=requirements/static/ci/py3.13/linux.txt -o=requirements/static/ci/py3.13/docs.txt +accessible-pygments==0.0.5 + # via pydata-sphinx-theme aiohappyeyeballs==2.6.1 # via # -c requirements/static/ci/py3.13/linux.txt @@ -27,7 +29,11 @@ autocommand==2.2.2 # -c requirements/static/ci/py3.13/linux.txt # jaraco-text babel==2.17.0 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx +beautifulsoup4==4.14.3 + # via pydata-sphinx-theme certifi==2026.1.4 # via # -c requirements/static/ci/py3.13/linux.txt @@ -74,7 +80,9 @@ distro==1.9.0 # -c requirements/static/ci/py3.13/linux.txt # -r requirements/base.txt docutils==0.22.4 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx filelock==3.20.3 # via # -c requirements/static/ci/py3.13/linux.txt @@ -217,11 +225,15 @@ pycryptodomex==3.23.0 # -c requirements/static/ci/py3.13/linux.txt # -r requirements/base.txt # -r requirements/crypto.txt +pydata-sphinx-theme==0.17.1 + # via -r requirements/static/ci/docs.in pyenchant==3.3.0 # via sphinxcontrib-spelling pygments==2.19.2 # via # -c requirements/static/ci/py3.13/linux.txt + # accessible-pygments + # pydata-sphinx-theme # sphinx pyopenssl==26.0.0 # via @@ -279,9 +291,12 @@ smmap==5.0.2 # gitdb snowballstemmer==3.0.1 # via sphinx +soupsieve==2.8.3 + # via beautifulsoup4 sphinx==9.1.0 # via # -r requirements/static/ci/docs.in + # pydata-sphinx-theme # sphinxcontrib-httpdomain # sphinxcontrib-spelling sphinxcontrib-applehelp==2.0.0 @@ -304,6 +319,11 @@ tempora==5.8.1 # via # -c requirements/static/ci/py3.13/linux.txt # portend +typing-extensions==4.15.0 + # via + # -c requirements/static/ci/py3.13/linux.txt + # beautifulsoup4 + # pydata-sphinx-theme uc-micro-py==1.0.3 # via linkify-it-py urllib3==2.6.3 diff --git a/requirements/static/ci/py3.9/docs.txt b/requirements/static/ci/py3.9/docs.txt index 073d3607fe5c..3408395950e2 100644 --- a/requirements/static/ci/py3.9/docs.txt +++ b/requirements/static/ci/py3.9/docs.txt @@ -1,5 +1,7 @@ # This file was autogenerated by uv via the following command: # uv pip compile requirements/base.txt requirements/zeromq.txt requirements/crypto.txt requirements/static/ci/docs.in --python-platform=linux --python-version=3.9 --constraint requirements/constraints.txt --no-emit-index-url --unsafe-package=setuptools -c=requirements/static/ci/py3.9/linux.txt -o=requirements/static/ci/py3.9/docs.txt +accessible-pygments==0.0.5 + # via pydata-sphinx-theme aiohappyeyeballs==2.6.1 # via # -c requirements/static/ci/py3.9/linux.txt @@ -31,11 +33,15 @@ autocommand==2.2.2 # -c requirements/static/ci/py3.9/linux.txt # jaraco-text babel==2.12.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx backports-tarfile==1.2.0 # via # -c requirements/static/ci/py3.9/linux.txt # jaraco-context +beautifulsoup4==4.14.3 + # via pydata-sphinx-theme certifi==2026.1.4 # via # -c requirements/static/ci/py3.9/linux.txt @@ -82,7 +88,9 @@ distro==1.8.0 # -c requirements/static/ci/py3.9/linux.txt # -r requirements/base.txt docutils==0.20.1 - # via sphinx + # via + # pydata-sphinx-theme + # sphinx filelock==3.19.1 # via # -c requirements/static/ci/py3.9/linux.txt @@ -230,11 +238,15 @@ pycryptodomex==3.23.0 # -c requirements/static/ci/py3.9/linux.txt # -r requirements/base.txt # -r requirements/crypto.txt +pydata-sphinx-theme==0.16.1 + # via -r requirements/static/ci/docs.in pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.19.2 # via # -c requirements/static/ci/py3.9/linux.txt + # accessible-pygments + # pydata-sphinx-theme # sphinx pyopenssl==26.0.0 # via @@ -289,9 +301,12 @@ smmap==5.0.2 # gitdb snowballstemmer==2.2.0 # via sphinx +soupsieve==2.8.3 + # via beautifulsoup4 sphinx==7.0.1 # via # -r requirements/static/ci/docs.in + # pydata-sphinx-theme # sphinxcontrib-httpdomain # sphinxcontrib-spelling sphinxcontrib-applehelp==1.0.4 @@ -318,8 +333,10 @@ typing-extensions==4.14.1 # via # -c requirements/static/ci/py3.9/linux.txt # aiosignal + # beautifulsoup4 # cryptography # gitpython + # pydata-sphinx-theme # pyopenssl # virtualenv uc-micro-py==1.0.2 diff --git a/salt/modules/gem.py b/salt/modules/gem.py index 700c302c888e..d54e0c320838 100644 --- a/salt/modules/gem.py +++ b/salt/modules/gem.py @@ -96,11 +96,11 @@ def install( Include pre-releases in the available versions :param proxy: string : None Use the specified HTTP proxy server for all outgoing traffic. - Format: http://hostname[:port] + Format: ``http://hostname:port`` source : None Use the specified HTTP gem source server to download gem. - Format: http://hostname[:port] + Format: ``http://hostname:port`` CLI Example: diff --git a/salt/states/gem.py b/salt/states/gem.py index 66477e60cff4..c57e87b19242 100644 --- a/salt/states/gem.py +++ b/salt/states/gem.py @@ -78,11 +78,11 @@ def installed( proxy : None Use the specified HTTP proxy server for all outgoing traffic. - Format: http://hostname[:port] + Format: ``http://hostname:port`` source : None Use the specified HTTP gem source server to download gem. - Format: http://hostname[:port] + Format: ``http://hostname:port`` """ ret = {"name": name, "result": None, "comment": "", "changes": {}} if ruby is not None and not ( diff --git a/salt/transport/zeromq.py b/salt/transport/zeromq.py index 69ea2710ffc7..cb8a64f80808 100644 --- a/salt/transport/zeromq.py +++ b/salt/transport/zeromq.py @@ -515,7 +515,7 @@ def __init__(self, opts, addr, linger=0, io_loop=None): :param dict opts: The salt opts dictionary :param str addr: The interface IP address to bind to :param int linger: The number of seconds to linger on a ZMQ socket. See - http://api.zeromq.org/2-1:zmq-setsockopt [ZMQ_LINGER] + http://api.zeromq.org/2-1:zmq-setsockopt ``[ZMQ_LINGER]`` :param IOLoop io_loop: A Tornado IOLoop event scheduler [tornado.ioloop.IOLoop] """ self.opts = opts