Skip to content

Commit

Permalink
feat!: Enable disallow_untyped_calls Mypy rule + drop Python 3.8 su…
Browse files Browse the repository at this point in the history
…pport (#1055)
  • Loading branch information
BoboTiG authored Aug 11, 2024
1 parent a318f39 commit 2872c7e
Show file tree
Hide file tree
Showing 25 changed files with 124 additions and 122 deletions.
16 changes: 8 additions & 8 deletions .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ task:
image_family: freebsd-12-2

install_script:
- pkg install -y python38 py38-sqlite3
- pkg install -y python39 py39-sqlite3
# Print the Python version, only to be sure we are running the version we want
- python3.8 -c 'import platform; print("Python", platform.python_version())'
- python3.9 -c 'import platform; print("Python", platform.python_version())'
# Check SQLite3 is installed
- python3.8 -c 'import sqlite3; print("SQLite3", sqlite3.version)'
- python3.9 -c 'import sqlite3; print("SQLite3", sqlite3.version)'
setup_script:
- python3.8 -m ensurepip
- python3.8 -m pip install -U pip
- python3.8 -m pip install -r requirements-tests.txt
- python3.9 -m ensurepip
- python3.9 -m pip install -U pip
- python3.9 -m pip install -r requirements-tests.txt
lint_script:
- python3.8 -m ruff src
- python3.9 -m ruff src
tests_script:
- python3.8 -bb -m pytest tests
- python3.9 -bb -m pytest tests
1 change: 0 additions & 1 deletion .github/workflows/build-and-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ jobs:
- name: Build wheels
run: python -m cibuildwheel
env:
CIBW_SKIP: "cp36-*" # skip 3.6 wheels
CIBW_ARCHS_MACOS: "x86_64 universal2 arm64"
- name: Artifacts list
run: ls -l wheelhouse
Expand Down
8 changes: 0 additions & 8 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,11 @@ jobs:
emoji: 🪟
runs-on: [windows-latest]
python:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13-dev"
- "pypy-3.8"
- "pypy-3.9"
include:
- tox:
Expand All @@ -67,15 +65,9 @@ jobs:
emoji: 🐧
runs-on: [ubuntu-latest]
exclude:
- os:
matrix: macos
python: "pypy-3.8"
- os:
matrix: macos
python: "pypy-3.9"
- os:
matrix: windows
python: "pypy-3.8"
- os:
matrix: windows
python: "pypy-3.9"
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Watchdog

Python API and shell utilities to monitor file system events.

Works on 3.8+.
Works on 3.9+.

Example API Usage
-----------------
Expand Down Expand Up @@ -211,7 +211,7 @@ appropriate observer like in the example above, do::
Dependencies
------------

1. Python 3.8 or above.
1. Python 3.9 or above.
2. XCode_ (only on macOS when installing from sources)
3. PyYAML_ (only for ``watchmedo``)

Expand Down
6 changes: 5 additions & 1 deletion changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ Changelog

2024-xx-xx • `full history <https://github.com/gorakhargosh/watchdog/compare/v4.0.2...HEAD>`__

-
- Drop support for Python 3.8 (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [core] Enable ``disallow_untyped_calls`` Mypy rule (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [core] Deleted the ``BaseObserverSubclassCallable`` class. Use ``type[BaseObserver]`` directly (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [inotify] Renamed the ``inotify_event_struct`` class to ``InotifyEventStruct`` (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- [windows] Renamed the ``FILE_NOTIFY_INFORMATION`` class to ``FileNotifyInformation`` (`#1055 <https://github.com/gorakhargosh/watchdog/pull/1055>`__)
- Thanks to our beloved contributors: @BoboTiG

4.0.2
Expand Down
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Watchdog

Python API library and shell utilities to monitor file system events.

Works on 3.8+.
Works on 3.9+.

Directory monitoring made easy with
-----------------------------------
Expand Down
2 changes: 1 addition & 1 deletion docs/source/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Installation
============
|project_name| requires 3.8+ to work. See a list of :ref:`installation-dependencies`.
|project_name| requires 3.9+ to work. See a list of :ref:`installation-dependencies`.

Installing from PyPI using pip
------------------------------
Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ follow_imports = "skip"
# Ensure full coverage
#disallow_untyped_defs = true [TODO]
disallow_incomplete_defs = true
#disallow_untyped_calls = true [TODO]
disallow_untyped_calls = true

# Restrict dynamic typing (a little)
# e.g. `x: List[Any]` or x: List`
Expand All @@ -31,7 +31,7 @@ addopts = """
[tool.ruff]
line-length = 120
indent-width = 4
target-version = "py38"
target-version = "py39"

[tool.ruff.lint]
extend-select = ["ALL"]
Expand All @@ -53,14 +53,14 @@ ignore = [
"FBT",
"FIX",
"ISC001",
"N", # Requires a major version number bump
"N818",
"PERF203", # TODO
"PL",
"PTH", # TODO?
"S",
"TD",
"TRY003",
"UP", # TODO when minimum python version will be 3.10
"UP",
]
fixable = ["ALL"]

Expand Down
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -155,6 +154,6 @@
"watchmedo = watchdog.watchmedo:main [watchmedo]",
]
},
python_requires=">=3.8",
python_requires=">=3.9",
zip_safe=False,
)
4 changes: 2 additions & 2 deletions src/watchdog/observers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@
from watchdog.utils import UnsupportedLibc, platform

if TYPE_CHECKING:
from watchdog.observers.api import BaseObserverSubclassCallable
from watchdog.observers.api import BaseObserver


def _get_observer_cls() -> BaseObserverSubclassCallable:
def _get_observer_cls() -> type[BaseObserver]:
if platform.is_linux():
with contextlib.suppress(UnsupportedLibc):
from watchdog.observers.inotify import InotifyObserver
Expand Down
6 changes: 1 addition & 5 deletions src/watchdog/observers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import threading
from pathlib import Path

from watchdog.utils import BaseThread, Protocol
from watchdog.utils import BaseThread
from watchdog.utils.bricks import SkipRepeatsQueue

DEFAULT_EMITTER_TIMEOUT = 1 # in seconds.
Expand Down Expand Up @@ -385,7 +385,3 @@ def dispatch_events(self, event_queue):
if handler in self._handlers.get(watch, []):
handler.dispatch(event)
event_queue.task_done()


class BaseObserverSubclassCallable(Protocol):
def __call__(self, timeout: float = ...) -> BaseObserver: ...
12 changes: 6 additions & 6 deletions src/watchdog/observers/fsevents2.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import unicodedata
import warnings
from threading import Thread
from typing import List, Optional, Type
from typing import Optional

# pyobjc
import AppKit
Expand Down Expand Up @@ -81,7 +81,7 @@ class FSEventsQueue(Thread):

def __init__(self, path):
Thread.__init__(self)
self._queue: queue.Queue[Optional[List[NativeEvent]]] = queue.Queue()
self._queue: queue.Queue[Optional[list[NativeEvent]]] = queue.Queue()
self._run_loop = None

if isinstance(path, bytes):
Expand Down Expand Up @@ -123,9 +123,9 @@ def stop(self):
if self._run_loop is not None:
CFRunLoopStop(self._run_loop)

def _callback(self, streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIDs):
events = [NativeEvent(path, flags, _id) for path, flags, _id in zip(eventPaths, eventFlags, eventIDs)]
logger.debug("FSEvents callback. Got %d events:", numEvents)
def _callback(self, stream_ref, client_callback_info, num_events, event_paths, event_flags, event_ids):
events = [NativeEvent(path, flags, _id) for path, flags, _id in zip(event_paths, event_flags, event_ids)]
logger.debug("FSEvents callback. Got %d events:", num_events)
for e in events:
logger.debug(e)
self._queue.put(events)
Expand Down Expand Up @@ -195,7 +195,7 @@ def queue_events(self, timeout):
while i < len(events):
event = events[i]

cls: Type[FileSystemEvent]
cls: type[FileSystemEvent]
# For some reason the create and remove flags are sometimes also
# set for rename and modify type events, so let those take
# precedence.
Expand Down
3 changes: 1 addition & 2 deletions src/watchdog/observers/inotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@
import logging
import os
import threading
from typing import Type

from watchdog.events import (
DirCreatedEvent,
Expand Down Expand Up @@ -141,7 +140,7 @@ def queue_events(self, timeout, full_events=False):
if event is None:
return

cls: Type[FileSystemEvent]
cls: type[FileSystemEvent]
if isinstance(event, tuple):
move_from, move_to = event
src_path = self._decode_path(move_from.src_path)
Expand Down
4 changes: 2 additions & 2 deletions src/watchdog/observers/inotify_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from __future__ import annotations

import logging
from typing import TYPE_CHECKING, List, Tuple, Union
from typing import TYPE_CHECKING, Union

from watchdog.observers.inotify_c import Inotify, InotifyEvent
from watchdog.utils import BaseThread
Expand Down Expand Up @@ -54,7 +54,7 @@ def close(self):

def _group_events(self, event_list):
"""Group any matching move events"""
grouped: List[Union[InotifyEvent, Tuple[InotifyEvent, InotifyEvent]]] = []
grouped: list[Union[InotifyEvent, tuple[InotifyEvent, InotifyEvent]]] = []
for inotify_event in event_list:
logger.debug("in-event %s", inotify_event)

Expand Down
4 changes: 2 additions & 2 deletions src/watchdog/observers/inotify_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class InotifyConstants:
)


class inotify_event_struct(ctypes.Structure):
class InotifyEventStruct(ctypes.Structure):
"""Structure representation of the inotify_event structure
(used in buffer size calculations)::
Expand All @@ -135,7 +135,7 @@ class inotify_event_struct(ctypes.Structure):
)


EVENT_SIZE = ctypes.sizeof(inotify_event_struct)
EVENT_SIZE = ctypes.sizeof(InotifyEventStruct)
DEFAULT_NUM_EVENTS = 2048
DEFAULT_EVENT_BUFFER_SIZE = DEFAULT_NUM_EVENTS * (EVENT_SIZE + 16)

Expand Down
22 changes: 11 additions & 11 deletions src/watchdog/observers/winapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def _errcheck_dword(value, func, args):
)


class FILE_NOTIFY_INFORMATION(ctypes.Structure):
class FileNotifyInformation(ctypes.Structure):
_fields_ = (
("NextEntryOffset", ctypes.wintypes.DWORD),
("Action", ctypes.wintypes.DWORD),
Expand All @@ -240,7 +240,7 @@ class FILE_NOTIFY_INFORMATION(ctypes.Structure):
)


LPFNI = ctypes.POINTER(FILE_NOTIFY_INFORMATION)
LPFNI = ctypes.POINTER(FileNotifyInformation)


# We don't need to recalculate these flags every time a call is made to
Expand Down Expand Up @@ -281,19 +281,19 @@ class FILE_NOTIFY_INFORMATION(ctypes.Structure):
PATH_BUFFER_SIZE = 2048


def _parse_event_buffer(readBuffer, nBytes):
def _parse_event_buffer(read_buffer, n_bytes):
results = []
while nBytes > 0:
fni = ctypes.cast(readBuffer, LPFNI)[0]
ptr = ctypes.addressof(fni) + FILE_NOTIFY_INFORMATION.FileName.offset
while n_bytes > 0:
fni = ctypes.cast(read_buffer, LPFNI)[0]
ptr = ctypes.addressof(fni) + FileNotifyInformation.FileName.offset
# filename = ctypes.wstring_at(ptr, fni.FileNameLength)
filename = ctypes.string_at(ptr, fni.FileNameLength)
results.append((fni.Action, filename.decode("utf-16")))
numToSkip = fni.NextEntryOffset
if numToSkip <= 0:
num_to_skip = fni.NextEntryOffset
if num_to_skip <= 0:
break
readBuffer = readBuffer[numToSkip:]
nBytes -= numToSkip # numToSkip is long. nBytes should be long too.
read_buffer = read_buffer[num_to_skip:]
n_bytes -= num_to_skip # numToSkip is long. nBytes should be long too.
return results


Expand All @@ -309,7 +309,7 @@ def _is_observed_path_deleted(handle, path):
def _generate_observed_path_deleted_event():
# Create synthetic event for notify that observed directory is deleted
path = ctypes.create_unicode_buffer(".")
event = FILE_NOTIFY_INFORMATION(0, FILE_ACTION_DELETED_SELF, len(path), path.value.encode("utf-8"))
event = FileNotifyInformation(0, FILE_ACTION_DELETED_SELF, len(path), path.value.encode("utf-8"))
event_size = ctypes.sizeof(event)
buff = ctypes.create_string_buffer(PATH_BUFFER_SIZE)
ctypes.memmove(buff, ctypes.addressof(event), event_size)
Expand Down
11 changes: 0 additions & 11 deletions src/watchdog/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

import sys
import threading
from typing import TYPE_CHECKING


class UnsupportedLibc(Exception):
Expand Down Expand Up @@ -130,13 +129,3 @@ def load_class(dotted_path):
# return klass(*args, **kwargs)

raise AttributeError(f"Module {module_name} does not have class attribute {klass_name}")


if TYPE_CHECKING or sys.version_info >= (3, 8):
# using `as` to explicitly re-export this since this is a compatibility layer
from typing import Protocol as Protocol
else:
# Provide a dummy Protocol class when not available from stdlib. Should be used
# only for hinting. This could be had from typing_protocol, but not worth adding
# the _first_ dependency just for this.
class Protocol: ...
4 changes: 2 additions & 2 deletions src/watchdog/utils/delayed_queue.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import threading
import time
from collections import deque
from typing import Callable, Deque, Generic, Optional, Tuple, TypeVar
from typing import Callable, Generic, Optional, TypeVar

T = TypeVar("T")

Expand All @@ -27,7 +27,7 @@ def __init__(self, delay):
self.delay_sec = delay
self._lock = threading.Lock()
self._not_empty = threading.Condition(self._lock)
self._queue: Deque[Tuple[T, float, bool]] = deque()
self._queue: deque[tuple[T, float, bool]] = deque()
self._closed = False

def put(self, element: T, delay: bool = False) -> None:
Expand Down
Loading

0 comments on commit 2872c7e

Please sign in to comment.