4949OPAQUE = 255
5050
5151
52+ # A sentinel value to indicate that a parameter was not passed, as opposed to being passed with a value of None. This
53+ # is used in the MSS constructor to distinguish between the user not passing a parameter, and the user explicitly
54+ # passing None (which is the default for some parameters). This allows us to preserve the existing behavior of ignoring
55+ # certain parameters on certain platforms, while still allowing users to explicitly set those parameters on platforms
56+ # where they are supported.
57+ class _PlatformSpecific :
58+ def __init__ (self , sphinx_repr : Any ) -> None :
59+ self .sphinx_repr = str (sphinx_repr )
60+
61+ def __repr__ (self ) -> str :
62+ # This is used to get Sphinx to show a useful default when it shows the default in the summary, rather than
63+ # an opaque object.
64+ return self .sphinx_repr
65+
66+
5267__all__ = ()
5368
5469
@@ -64,14 +79,12 @@ class MSSImplementation(ABC):
6479 with_cursor : bool
6580
6681 def __init__ (self , / , * , with_cursor : bool = False ) -> None :
67- # We put with_cursor on the MSSImplementation because the Xlib
68- # backend will turn it off if the library isn't installed.
69- # (It's not a separate library under XCB.) So, we need to let
70- # the backend mutate it.
71-
72- # We should remove this expectation in 11.0. It seems
73- # unlikely to be practically useful, Xlib is legacy, and just
74- # complicates things.
82+ # We put with_cursor on the MSSImplementation because the Xlib backend will turn it off if the library isn't
83+ # installed. (It's not a separate library under XCB.) So, we need to let the backend mutate it. Note that
84+ # the other platforms don't support with_cursor, and don't pass it to us.
85+ #
86+ # TODO(jholveck): #493 We should remove this expectation in 11.0. It seems unlikely to be practically useful,
87+ # Xlib is legacy, and just complicates things.
7588 self .with_cursor = with_cursor
7689
7790 @abstractmethod
@@ -156,43 +169,67 @@ class MSS:
156169
157170 :param backend: Backend selector, for platforms with multiple backends.
158171 :param compression_level: PNG compression level.
159- :param with_cursor: Include the mouse cursor in screenshots.
172+ :param with_cursor: Include the mouse cursor in screenshots (GNU/Linux only)
173+ :type display: bool, optional (default False)
160174 :param display: X11 display name (GNU/Linux only).
161175 :type display: bytes | str, optional (default :envvar:`$DISPLAY`)
162176 :param max_displays: Maximum number of displays to enumerate (macOS only).
163177 :type max_displays: int, optional (default 32)
164178
165179 .. versionadded:: 8.0.0
166180 ``compression_level``, ``display``, ``max_displays``, and ``with_cursor`` keyword arguments.
181+
182+ .. versionadded:: 10.2.0
183+ ``backend`` keyword argument.
167184 """
168185
186+ # We want to:
187+ # * Let Sphinx, IDEs, and code-checkers know all the possible kwargs.
188+ # * Know if a user explicitly passed an unsupported platform-dependent keyword, so we can warn.
189+ # * Show a meaningful default in the Sphinx doc's summary string
190+ #
191+ # To accomplish this:
192+ # * We list the possibilities explicitly in the __init__ kwargs.
193+ # * We use a sentinel value, so we can tell whether or not the user actually gave us a value.
194+ # * We represent the "default value" sentinel object with something different, so Sphinx formats it usefully.
195+ _PD_WITH_CURSOR = _PlatformSpecific (False ) # noqa: FBT003
196+ _PD_DISPLAY = _PlatformSpecific (None )
197+ _PD_MAX_DISPLAYS = _PlatformSpecific (32 )
198+
169199 def __init__ (
170200 self ,
171201 / ,
172202 * ,
173203 backend : str = "default" ,
174204 compression_level : int = 6 ,
175- ** kwargs : Any ,
205+ with_cursor : bool | _PlatformSpecific = _PD_WITH_CURSOR ,
206+ display : bytes | str | None | _PlatformSpecific = _PD_DISPLAY ,
207+ max_displays : int | _PlatformSpecific = _PD_MAX_DISPLAYS ,
176208 ) -> None :
177- # TODO(jholveck): #493 Accept platform-specific kwargs on all platforms for migration ease. Foreign kwargs
178- # are silently stripped with a warning.
179- platform_only : dict [str , str ] = {
180- "display" : "linux" ,
181- "max_displays" : "darwin" ,
182- }
183- os_ = platform .system ().lower ()
184- for kwarg_name , target_platform in platform_only .items ():
185- if kwarg_name in kwargs and os_ != target_platform :
186- kwargs .pop (kwarg_name )
209+ impl_kwargs = {}
210+
211+ system = platform .system ().lower ()
212+ for name , value , supported_platform in [
213+ ("with_cursor" , with_cursor , "Linux" ),
214+ ("display" , display , "Linux" ),
215+ ("max_displays" , max_displays , "Darwin" ),
216+ ]:
217+ if isinstance (value , _PlatformSpecific ):
218+ continue
219+ if system != supported_platform .lower ():
220+ # TODO(jholveck): #493 Accept platform-specific kwargs on all platforms for migration ease. Foreign
221+ # kwargs are silently stripped with a warning.
187222 warnings .warn (
188- f"{ kwarg_name } is only used on { target_platform } . This will be an error in the future." ,
223+ f"{ name } is only available on { supported_platform } . This will be an error in the future." ,
189224 DeprecationWarning ,
190225 stacklevel = 2 ,
191226 )
227+ else :
228+ impl_kwargs [name ] = value
192229
193230 self ._impl : MSSImplementation = _choose_impl (
194231 backend = backend ,
195- ** kwargs ,
232+ ** impl_kwargs ,
196233 )
197234
198235 # The cls_image is only used atomically, so does not require locking.
0 commit comments