Skip to content

Commit 708103d

Browse files
authored
Merge branch 'master' into mk/remove_job_from_dependency_dependent_keys
2 parents a0e6dab + 17c5fb3 commit 708103d

File tree

10 files changed

+109
-84
lines changed

10 files changed

+109
-84
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### RQ 2.3.3 (2025-05-10)
2+
* `WorkerPool` now accepts `queue_class` argument. Thanks @amonsh1!
3+
* Disallow `redis-py=6.0.0`. Thanks @selwin and @terencehonles!
4+
* Minor typing improvements. Thanks @SpecLad!
5+
16
### RQ 2.3.2 (2025-04-13)
27
* Don't log job description when `log_job_description` is set to False. Thanks @danilopeixoto!
38
* Fixes an issue where `pubsub_thread` may die in the background. Thanks @ankush!

SECURITY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Security contact information
2+
3+
To report a security vulnerability, please use the
4+
[Tidelift security contact](https://tidelift.com/security).
5+
Tidelift will coordinate the fix and disclosure.

pyproject.toml

Lines changed: 71 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,46 @@
11
[build-system]
22
build-backend = "hatchling.build"
3-
requires = [
4-
"hatchling",
5-
]
3+
requires = ["hatchling"]
64

75
[project]
86
name = "rq"
97
description = "RQ is a simple, lightweight, library for creating background jobs, and processing them."
108
readme = "README.md"
119
license = "BSD-2-Clause"
12-
maintainers = [
13-
{ name = "Selwin Ong" },
14-
]
10+
maintainers = [{ name = "Selwin Ong" }]
1511
authors = [
16-
{ name = "Selwin Ong", email = "[email protected]" },
17-
{ name = "Vincent Driessen", email = "[email protected]" },
12+
{ name = "Selwin Ong", email = "[email protected]" },
13+
{ name = "Vincent Driessen", email = "[email protected]" },
1814
]
1915
requires-python = ">=3.8"
2016
classifiers = [
21-
"Development Status :: 5 - Production/Stable",
22-
"Intended Audience :: Developers",
23-
"Intended Audience :: End Users/Desktop",
24-
"Intended Audience :: Information Technology",
25-
"Intended Audience :: Science/Research",
26-
"Intended Audience :: System Administrators",
27-
"License :: OSI Approved :: BSD License",
28-
"Operating System :: MacOS",
29-
"Operating System :: POSIX",
30-
"Operating System :: Unix",
31-
"Programming Language :: Python",
32-
"Programming Language :: Python :: 3 :: Only",
33-
"Programming Language :: Python :: 3.8",
34-
"Programming Language :: Python :: 3.9",
35-
"Programming Language :: Python :: 3.10",
36-
"Programming Language :: Python :: 3.11",
37-
"Programming Language :: Python :: 3.12",
38-
"Programming Language :: Python :: 3.13",
39-
"Topic :: Internet",
40-
"Topic :: Scientific/Engineering",
41-
"Topic :: Software Development :: Libraries :: Python Modules",
42-
"Topic :: System :: Distributed Computing",
43-
"Topic :: System :: Monitoring",
44-
"Topic :: System :: Systems Administration",
45-
]
46-
dynamic = [
47-
"version",
48-
]
49-
dependencies = [
50-
"click>=5",
51-
"redis>=3.5",
17+
"Development Status :: 5 - Production/Stable",
18+
"Intended Audience :: Developers",
19+
"Intended Audience :: End Users/Desktop",
20+
"Intended Audience :: Information Technology",
21+
"Intended Audience :: Science/Research",
22+
"Intended Audience :: System Administrators",
23+
"License :: OSI Approved :: BSD License",
24+
"Operating System :: MacOS",
25+
"Operating System :: POSIX",
26+
"Operating System :: Unix",
27+
"Programming Language :: Python",
28+
"Programming Language :: Python :: 3 :: Only",
29+
"Programming Language :: Python :: 3.8",
30+
"Programming Language :: Python :: 3.9",
31+
"Programming Language :: Python :: 3.10",
32+
"Programming Language :: Python :: 3.11",
33+
"Programming Language :: Python :: 3.12",
34+
"Programming Language :: Python :: 3.13",
35+
"Topic :: Internet",
36+
"Topic :: Scientific/Engineering",
37+
"Topic :: Software Development :: Libraries :: Python Modules",
38+
"Topic :: System :: Distributed Computing",
39+
"Topic :: System :: Monitoring",
40+
"Topic :: System :: Systems Administration",
5241
]
42+
dynamic = ["version"]
43+
dependencies = ["click>=5", "redis>=3.5,!=6.0.0"]
5344
urls.changelog = "https://github.com/rq/rq/blob/master/CHANGES.md"
5445
urls.documentation = "https://python-rq.org/docs/"
5546
urls.homepage = "https://python-rq.org/"
@@ -60,48 +51,48 @@ scripts.rqworker = "rq.cli:worker" # TODO [v2]: Remove
6051

6152
[dependency-groups]
6253
dev = [
63-
"coverage>=7.6.1",
64-
"mypy>=1.14.1",
65-
"packaging>=24.2",
66-
"psutil>=7",
67-
"pytest>=8.3.5",
68-
"pytest-cov>=5",
69-
"ruff>=0.9.9",
70-
"tox>=4.24.1",
71-
"types-greenlet>=3.1.0.20241221",
72-
"types-redis>=4.6.0.20241004",
54+
"coverage>=7.6.1",
55+
"mypy>=1.14.1",
56+
"packaging>=24.2",
57+
"psutil>=7",
58+
"pytest>=8.3.5",
59+
"pytest-cov>=5",
60+
"ruff>=0.9.9",
61+
"tox>=4.24.1",
62+
"types-greenlet>=3.1.0.20241221",
63+
"types-redis>=4.6.0.20241004",
7364
]
7465

7566
[tool.hatch.version]
7667
path = "rq/version.py"
7768

7869
[tool.hatch.build.targets.sdist]
7970
include = [
80-
"/docs",
81-
"/rq",
82-
"/tests",
83-
"CHANGES.md",
84-
"LICENSE",
85-
"pyproject.toml",
86-
"README.md",
87-
"requirements.txt",
88-
"tox.ini",
71+
"/docs",
72+
"/rq",
73+
"/tests",
74+
"CHANGES.md",
75+
"LICENSE",
76+
"pyproject.toml",
77+
"README.md",
78+
"requirements.txt",
79+
"tox.ini",
8980
]
9081
[tool.hatch.envs.default]
9182
installer = "uv"
9283

9384
[tool.hatch.envs.test]
9485
dependencies = [
95-
"coverage",
96-
"mypy",
97-
"packaging",
98-
"psutil",
99-
"pytest",
100-
"pytest-cov",
101-
"ruff",
102-
"tox",
103-
"types-greenlet",
104-
"types-redis",
86+
"coverage",
87+
"mypy",
88+
"packaging",
89+
"psutil",
90+
"pytest",
91+
"pytest-cov",
92+
"ruff",
93+
"tox",
94+
"types-greenlet",
95+
"types-redis",
10596
]
10697
[tool.hatch.envs.test.scripts]
10798
cov = "pytest --cov=rq --cov-config=.coveragerc --cov-report=xml {args:tests}"
@@ -115,13 +106,19 @@ target-version = "py38"
115106
line-length = 120
116107
format.quote-style = "single"
117108
lint.select = [
118-
"E", # pycodestyle errors
119-
"F", # pyflakes errors
120-
"I", # import sorting
121-
"W", # pycodestyle warnings
109+
"E", # pycodestyle errors
110+
"F", # pyflakes errors
111+
"I", # import sorting
112+
"W", # pycodestyle warnings
113+
]
114+
lint.isort.known-first-party = ["rq"]
115+
lint.isort.section-order = [
116+
"future",
117+
"standard-library",
118+
"third-party",
119+
"first-party",
120+
"local-folder",
122121
]
123-
lint.isort.known-first-party = [ "rq" ]
124-
lint.isort.section-order = [ "future", "standard-library", "third-party", "first-party", "local-folder" ]
125122

126123
[tool.mypy]
127124
allow_redefinition = true

rq/queue.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ def prepare_data(
757757
ttl: Optional[int] = None,
758758
failure_ttl: Optional[int] = None,
759759
description: Optional[str] = None,
760-
depends_on: Optional[List] = None,
760+
depends_on: Optional['JobDependencyType'] = None,
761761
job_id: Optional[str] = None,
762762
at_front: bool = False,
763763
meta: Optional[Dict] = None,

rq/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
VERSION = '2.3.2'
1+
VERSION = '2.3.3'

rq/worker.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,7 +1728,12 @@ def fork_work_horse(self, job: 'Job', queue: 'Queue'):
17281728
"""Spawns a work horse to perform the actual work using os.spawn()."""
17291729
os.environ['RQ_WORKER_ID'] = self.name
17301730
os.environ['RQ_JOB_ID'] = job.id
1731-
connection_kwargs = {k: v for k, v in self.connection.connection_pool.connection_kwargs.items() if k != 'retry'}
1731+
1732+
redis_kwargs = self.connection.connection_pool.connection_kwargs
1733+
if redis_kwargs.get('retry'):
1734+
# Remove retry from connection kwargs to avoid issues with os.spawnv
1735+
del redis_kwargs['retry']
1736+
17321737
child_pid = os.spawnv(
17331738
os.P_NOWAIT,
17341739
sys.executable,
@@ -1743,7 +1748,7 @@ def fork_work_horse(self, job: 'Job', queue: 'Queue'):
17431748
from rq.job import Job
17441749
17451750
# Recreate worker instance
1746-
redis = Redis(**{connection_kwargs})
1751+
redis = Redis(**{redis_kwargs})
17471752
worker = Worker.find_by_key("{self.key}", connection=redis)
17481753
if not worker:
17491754
sys.exit(1)

rq/worker_pool.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def __init__(
4747
worker_class: Type[BaseWorker] = Worker,
4848
serializer: Type['Serializer'] = DefaultSerializer,
4949
job_class: Type[Job] = Job,
50+
queue_class: Type[Queue] = Queue,
5051
*args,
5152
**kwargs,
5253
):
@@ -64,6 +65,7 @@ def __init__(
6465
self.worker_class: Type[BaseWorker] = worker_class
6566
self.serializer: Type['Serializer'] = serializer
6667
self.job_class: Type[Job] = job_class
68+
self.queue_class: Type[Queue] = queue_class
6769

6870
# A dictionary of WorkerData keyed by worker name
6971
self.worker_dict: Dict[str, WorkerData] = {}
@@ -72,7 +74,7 @@ def __init__(
7274
@property
7375
def queues(self) -> List[Queue]:
7476
"""Returns a list of Queue objects"""
75-
return [Queue(name, connection=self.connection) for name in self._queue_names]
77+
return [self.queue_class(name, connection=self.connection) for name in self._queue_names]
7678

7779
@property
7880
def number_of_active_workers(self) -> int:
@@ -251,15 +253,23 @@ def run_worker(
251253
worker_class: Type[BaseWorker] = Worker,
252254
serializer: Type['Serializer'] = DefaultSerializer,
253255
job_class: Type[Job] = Job,
256+
queue_class: Type[Queue] = Queue,
254257
burst: bool = True,
255258
logging_level: str = 'INFO',
256259
_sleep: int = 0,
257260
):
258261
connection = connection_class(
259262
connection_pool=ConnectionPool(connection_class=connection_pool_class, **connection_pool_kwargs)
260263
)
261-
queues = [Queue(name, connection=connection) for name in queue_names]
262-
worker = worker_class(queues, name=worker_name, connection=connection, serializer=serializer, job_class=job_class)
264+
queues = [queue_class(name, connection=connection) for name in queue_names]
265+
worker = worker_class(
266+
queues,
267+
name=worker_name,
268+
connection=connection,
269+
serializer=serializer,
270+
job_class=job_class,
271+
queue_class=queue_class,
272+
)
263273
worker.log.info('Starting worker started with PID %s', os.getpid())
264274
time.sleep(_sleep)
265275
worker.work(burst=burst, with_scheduler=True, logging_level=logging_level)

tests/test_spawn_worker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def test_working_worker_cold_shutdown(self):
106106

107107
sentinel_file = '/tmp/.rq_sentinel_cold'
108108
self.assertFalse(
109-
os.path.exists(sentinel_file), '{sentinel_file} file should not exist yet, delete that file and try again.'
109+
os.path.exists(sentinel_file), f'{sentinel_file} file should not exist yet, delete that file and try again.'
110110
)
111111
fooq.enqueue(create_file_after_timeout, sentinel_file, 5)
112112
self.assertFalse(w._stop_requested)

tests/test_worker.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,7 @@ def test_working_worker_cold_shutdown(self):
14231423

14241424
sentinel_file = '/tmp/.rq_sentinel_cold'
14251425
self.assertFalse(
1426-
os.path.exists(sentinel_file), '{sentinel_file} file should not exist yet, delete that file and try again.'
1426+
os.path.exists(sentinel_file), f'{sentinel_file} file should not exist yet, delete that file and try again.'
14271427
)
14281428
fooq.enqueue(create_file_after_timeout, sentinel_file, 5)
14291429
self.assertFalse(w._stop_requested)
@@ -1442,7 +1442,7 @@ def test_working_worker_cold_shutdown(self):
14421442

14431443
@slow
14441444
def test_work_horse_death_sets_job_failed(self):
1445-
"""worker with an ongoing job whose work horse dies unexpectadly (before
1445+
"""worker with an ongoing job whose work horse dies unexpectedly (before
14461446
completing the job) should set the job's status to FAILED
14471447
"""
14481448
fooq = Queue('foo', connection=self.connection)

tox.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,6 @@ deps=
5252
passenv=
5353
RUN_SSL_TESTS
5454
commands=pytest -m ssl_test {posargs}
55+
56+
[pytest]
57+
filterwarnings = ignore::pytest.PytestUnhandledThreadExceptionWarning

0 commit comments

Comments
 (0)