Skip to content

Commit 1c18df3

Browse files
committed
refactor!: the whole source code was migrated to PEP 570 (Python Positional-Only Parameters)
Dropped Python 3.7 support at the same time.
1 parent e2c5136 commit 1c18df3

File tree

14 files changed

+47
-107
lines changed

14 files changed

+47
-107
lines changed

.github/workflows/tests.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ jobs:
5656
- emoji: 🪟
5757
runs-on: [windows-latest]
5858
python:
59-
- name: CPython 3.7
60-
runs-on: "3.7"
6159
- name: CPython 3.8
6260
runs-on: "3.8"
6361
- name: CPython 3.9

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ History:
33
<see Git checking messages for history>
44

55
8.0.0 2023/0x/xx
6+
- the whole source code was migrated to PEP 570 (Python Positional-Only Parameters)
7+
- removed support for Python 3.7
68
- Linux: added mouse support (#232)
79
- Linux: refactored how internal handles are stored to fix issues with multiple X servers, and TKinter.
810
No more side effects, and when leaving the context manager, resources are all freed (#224, #234)

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Python MSS
2222
2323
An ultra fast cross-platform multiple screenshots module in pure python using ctypes.
2424

25-
- **Python 3.7+** and PEP8 compliant, no dependency, thread-safe;
25+
- **Python 3.8+** and PEP8 compliant, no dependency, thread-safe;
2626
- very basic, it will grab one screen shot by monitor or a screen shot of all monitors and save it to a PNG file;
2727
- but you can use PIL and benefit from all its formats (or add yours directly);
2828
- integrate well with Numpy and OpenCV;

docs/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Welcome to Python MSS's documentation!
1212
1313
An ultra fast cross-platform multiple screenshots module in pure python using ctypes.
1414

15-
- **Python 3.7+** and :pep:`8` compliant, no dependency, thread-safe;
15+
- **Python 3.8+** and :pep:`8` compliant, no dependency, thread-safe;
1616
- very basic, it will grab one screen shot by monitor or a screen shot of all monitors and save it to a PNG file;
1717
- but you can use PIL and benefit from all its formats (or add yours directly);
1818
- integrate well with Numpy and OpenCV;

docs/source/support.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Support
55
Feel free to try MSS on a system we had not tested, and let us know by creating an `issue <https://github.com/BoboTiG/python-mss/issues>`_.
66

77
- OS: GNU/Linux, macOS and Windows
8-
- Python: 3.7 and newer
8+
- Python: 3.8 and newer
99

1010

1111
Future
@@ -32,4 +32,5 @@ Abandoned
3232
- Python 3.3 (2017-12-05)
3333
- Python 3.4 (2018-03-19)
3434
- Python 3.5 (2022-10-27)
35-
- Python 3.6 (202x-xx-xx)
35+
- Python 3.6 (2022-10-27)
36+
- Python 3.7 (2023-xx-xx)

mss/__main__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"""
55
import os.path
66
from argparse import ArgumentParser
7-
from typing import List, Optional
7+
from typing import List
88

99
from . import __version__
1010
from .exception import ScreenShotError
1111
from .factory import mss
1212
from .tools import to_png
1313

1414

15-
def main(args: Optional[List[str]] = None) -> int:
15+
def main(args: List[str], /) -> int:
1616
"""Main logic."""
1717

1818
cli_args = ArgumentParser()

mss/base.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class MSSBase(metaclass=ABCMeta):
2222

2323
def __init__(
2424
self,
25+
/,
26+
*,
2527
compression_level: int = 6,
2628
display: Optional[Union[bytes, str]] = None, # Linux only
2729
max_displays: int = 32, # Mac only
@@ -48,7 +50,7 @@ def _cursor_impl(self) -> Optional[ScreenShot]:
4850
"""Retrieve all cursor data. Pixels have to be RGB."""
4951

5052
@abstractmethod
51-
def _grab_impl(self, monitor: Monitor) -> ScreenShot:
53+
def _grab_impl(self, monitor: Monitor, /) -> ScreenShot:
5254
"""
5355
Retrieve all pixels from a monitor. Pixels have to be RGB.
5456
That method has to be run using a threading lock.
@@ -64,7 +66,7 @@ def _monitors_impl(self) -> None:
6466
def close(self) -> None:
6567
"""Clean-up."""
6668

67-
def grab(self, monitor: Union[Monitor, Tuple[int, int, int, int]]) -> ScreenShot:
69+
def grab(self, monitor: Union[Monitor, Tuple[int, int, int, int]], /) -> ScreenShot:
6870
"""
6971
Retrieve screen pixels for a given monitor.
7072
@@ -120,6 +122,8 @@ def monitors(self) -> Monitors:
120122

121123
def save(
122124
self,
125+
/,
126+
*,
123127
mon: int = 0,
124128
output: str = "monitor-{mon}.png",
125129
callback: Optional[Callable[[str], None]] = None,
@@ -170,9 +174,8 @@ def save(
170174
mon = 0 if mon == -1 else mon
171175
try:
172176
monitor = monitors[mon]
173-
except IndexError:
174-
# pylint: disable=raise-missing-from
175-
raise ScreenShotError(f"Monitor {mon!r} does not exist.")
177+
except IndexError as exc:
178+
raise ScreenShotError(f"Monitor {mon!r} does not exist.") from exc
176179

177180
output = output.format(mon=mon, date=datetime.now(), **monitor)
178181
if callable(callback):
@@ -181,7 +184,7 @@ def save(
181184
to_png(sct.rgb, sct.size, level=self.compression_level, output=output)
182185
yield output
183186

184-
def shot(self, **kwargs: Any) -> str:
187+
def shot(self, /, **kwargs: Any) -> str:
185188
"""
186189
Helper to save the screen shot of the 1st monitor, by default.
187190
You can pass the same arguments as for ``save``.
@@ -191,7 +194,7 @@ def shot(self, **kwargs: Any) -> str:
191194
return next(self.save(**kwargs))
192195

193196
@staticmethod
194-
def _merge(screenshot: ScreenShot, cursor: ScreenShot) -> ScreenShot:
197+
def _merge(screenshot: ScreenShot, cursor: ScreenShot, /) -> ScreenShot:
195198
"""Create composite image by blending screenshot and mouse cursor."""
196199

197200
# pylint: disable=too-many-locals,invalid-name
@@ -244,6 +247,7 @@ def _cfactory(
244247
func: str,
245248
argtypes: List[Any],
246249
restype: Any,
250+
/,
247251
errcheck: Optional[Callable] = None,
248252
) -> None:
249253
"""Factory to create a ctypes function and automatically manage errors."""

mss/darwin.py

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,15 @@ def __repr__(self) -> str:
6666
"CFDataGetLength": ("core", [c_void_p], c_uint64),
6767
"CFRelease": ("core", [c_void_p], c_void_p),
6868
"CGDataProviderRelease": ("core", [c_void_p], c_void_p),
69-
"CGGetActiveDisplayList": (
70-
"core",
71-
[c_uint32, POINTER(c_uint32), POINTER(c_uint32)],
72-
c_int32,
73-
),
69+
"CGGetActiveDisplayList": ("core", [c_uint32, POINTER(c_uint32), POINTER(c_uint32)], c_int32),
7470
"CGImageGetBitsPerPixel": ("core", [c_void_p], int),
7571
"CGImageGetBytesPerRow": ("core", [c_void_p], int),
7672
"CGImageGetDataProvider": ("core", [c_void_p], c_void_p),
7773
"CGImageGetHeight": ("core", [c_void_p], int),
7874
"CGImageGetWidth": ("core", [c_void_p], int),
7975
"CGRectStandardize": ("core", [CGRect], CGRect),
8076
"CGRectUnion": ("core", [CGRect, CGRect], CGRect),
81-
"CGWindowListCreateImage": (
82-
"core",
83-
[CGRect, c_uint32, c_uint32, c_uint32],
84-
c_void_p,
85-
),
77+
"CGWindowListCreateImage": ("core", [CGRect, c_uint32, c_uint32, c_uint32], c_void_p),
8678
}
8779

8880

@@ -94,7 +86,7 @@ class MSS(MSSBase):
9486

9587
__slots__ = {"core", "max_displays"}
9688

97-
def __init__(self, **kwargs: Any) -> None:
89+
def __init__(self, /, **kwargs: Any) -> None:
9890
"""macOS initialisations."""
9991

10092
super().__init__(**kwargs)
@@ -124,12 +116,7 @@ def _set_cfunctions(self) -> None:
124116
cfactory = self._cfactory
125117
attrs = {"core": self.core}
126118
for func, (attr, argtypes, restype) in CFUNCTIONS.items():
127-
cfactory(
128-
attr=attrs[attr],
129-
func=func,
130-
argtypes=argtypes,
131-
restype=restype,
132-
)
119+
cfactory(attrs[attr], func, argtypes, restype)
133120

134121
def _monitors_impl(self) -> None:
135122
"""Get positions of monitors. It will populate self._monitors."""
@@ -176,7 +163,7 @@ def _monitors_impl(self) -> None:
176163
"height": int_(all_monitors.size.height),
177164
}
178165

179-
def _grab_impl(self, monitor: Monitor) -> ScreenShot:
166+
def _grab_impl(self, monitor: Monitor, /) -> ScreenShot:
180167
"""Retrieve all pixels from a monitor. Pixels have to be RGB."""
181168

182169
# pylint: disable=too-many-locals

mss/exception.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
class ScreenShotError(Exception):
99
"""Error handling class."""
1010

11-
def __init__(self, message: str, details: Optional[Dict[str, Any]] = None) -> None:
11+
def __init__(self, message: str, /, *, details: Optional[Dict[str, Any]] = None) -> None:
1212
super().__init__(message)
1313
self.details = details or {}

mss/linux.py

Lines changed: 11 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ def _error_handler(display: Display, event: Event) -> int:
210210
return 0
211211

212212

213-
def _validate(retval: int, func: Any, args: Tuple[Any, Any]) -> Tuple[Any, Any]:
213+
def _validate(retval: int, func: Any, args: Tuple[Any, Any], /) -> Tuple[Any, Any]:
214214
"""Validate the returned value of a Xlib or XRANDR function."""
215215

216216
thread = current_thread()
@@ -237,52 +237,17 @@ def _validate(retval: int, func: Any, args: Tuple[Any, Any]) -> Tuple[Any, Any]:
237237
"XFixesGetCursorImage": ("xfixes", [POINTER(Display)], POINTER(XFixesCursorImage)),
238238
"XGetImage": (
239239
"xlib",
240-
[
241-
POINTER(Display),
242-
POINTER(Display),
243-
c_int,
244-
c_int,
245-
c_uint,
246-
c_uint,
247-
c_ulong,
248-
c_int,
249-
],
240+
[POINTER(Display), POINTER(Display), c_int, c_int, c_uint, c_uint, c_ulong, c_int],
250241
POINTER(XImage),
251242
),
252-
"XGetWindowAttributes": (
253-
"xlib",
254-
[POINTER(Display), POINTER(XWindowAttributes), POINTER(XWindowAttributes)],
255-
c_int,
256-
),
243+
"XGetWindowAttributes": ("xlib", [POINTER(Display), POINTER(XWindowAttributes), POINTER(XWindowAttributes)], c_int),
257244
"XOpenDisplay": ("xlib", [c_char_p], POINTER(Display)),
258-
"XQueryExtension": (
259-
"xlib",
260-
[
261-
POINTER(Display),
262-
c_char_p,
263-
POINTER(c_int),
264-
POINTER(c_int),
265-
POINTER(c_int),
266-
],
267-
c_uint,
268-
),
245+
"XQueryExtension": ("xlib", [POINTER(Display), c_char_p, POINTER(c_int), POINTER(c_int), POINTER(c_int)], c_uint),
269246
"XRRFreeCrtcInfo": ("xrandr", [POINTER(XRRCrtcInfo)], c_void_p),
270247
"XRRFreeScreenResources": ("xrandr", [POINTER(XRRScreenResources)], c_void_p),
271-
"XRRGetCrtcInfo": (
272-
"xrandr",
273-
[POINTER(Display), POINTER(XRRScreenResources), c_long],
274-
POINTER(XRRCrtcInfo),
275-
),
276-
"XRRGetScreenResources": (
277-
"xrandr",
278-
[POINTER(Display), POINTER(Display)],
279-
POINTER(XRRScreenResources),
280-
),
281-
"XRRGetScreenResourcesCurrent": (
282-
"xrandr",
283-
[POINTER(Display), POINTER(Display)],
284-
POINTER(XRRScreenResources),
285-
),
248+
"XRRGetCrtcInfo": ("xrandr", [POINTER(Display), POINTER(XRRScreenResources), c_long], POINTER(XRRCrtcInfo)),
249+
"XRRGetScreenResources": ("xrandr", [POINTER(Display), POINTER(Display)], POINTER(XRRScreenResources)),
250+
"XRRGetScreenResourcesCurrent": ("xrandr", [POINTER(Display), POINTER(Display)], POINTER(XRRScreenResources)),
286251
"XSetErrorHandler": ("xlib", [c_void_p], c_int),
287252
}
288253

@@ -295,7 +260,7 @@ class MSS(MSSBase):
295260

296261
__slots__ = {"xfixes", "xlib", "xrandr", "_handles"}
297262

298-
def __init__(self, **kwargs: Any) -> None:
263+
def __init__(self, /, **kwargs: Any) -> None:
299264
"""GNU/Linux initialisations."""
300265

301266
super().__init__(**kwargs)
@@ -355,7 +320,7 @@ def close(self) -> None:
355320

356321
_ERROR.clear()
357322

358-
def _is_extension_enabled(self, name: str) -> bool:
323+
def _is_extension_enabled(self, name: str, /) -> bool:
359324
"""Return True if the given *extension* is enabled on the server."""
360325
with lock:
361326
major_opcode_return = c_int()
@@ -385,13 +350,7 @@ def _set_cfunctions(self) -> None:
385350
}
386351
for func, (attr, argtypes, restype) in CFUNCTIONS.items():
387352
with suppress(AttributeError):
388-
cfactory(
389-
attr=attrs[attr],
390-
errcheck=_validate,
391-
func=func,
392-
argtypes=argtypes,
393-
restype=restype,
394-
)
353+
cfactory(attrs[attr], func, argtypes, restype, errcheck=_validate)
395354

396355
def _monitors_impl(self) -> None:
397356
"""Get positions of monitors. It will populate self._monitors."""
@@ -435,7 +394,7 @@ def _monitors_impl(self) -> None:
435394
xrandr.XRRFreeCrtcInfo(crtc)
436395
xrandr.XRRFreeScreenResources(mon)
437396

438-
def _grab_impl(self, monitor: Monitor) -> ScreenShot:
397+
def _grab_impl(self, monitor: Monitor, /) -> ScreenShot:
439398
"""Retrieve all pixels from a monitor. Pixels have to be RGB."""
440399

441400
ximage = self.xlib.XGetImage(

0 commit comments

Comments
 (0)