Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Enable disallow_untyped_calls Mypy rule + drop Python 3.8 support #1055

Merged
merged 4 commits into from
Aug 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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