python-pq 0.5.1__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. {python_pq-0.5.1 → python_pq-0.5.2}/PKG-INFO +15 -1
  2. {python_pq-0.5.1 → python_pq-0.5.2}/README.md +14 -0
  3. {python_pq-0.5.1 → python_pq-0.5.2}/pyproject.toml +1 -1
  4. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/__init__.py +1 -1
  5. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/client.py +5 -0
  6. python_pq-0.5.2/src/pq/migrations/versions/20260217T120000Z_c3d4e5f6a7b8_add_periodic_active.py +32 -0
  7. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/models.py +2 -0
  8. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/worker.py +1 -0
  9. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/config.py +0 -0
  10. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/logging.py +0 -0
  11. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/README +0 -0
  12. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/__init__.py +0 -0
  13. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/env.py +0 -0
  14. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/script.py.mako +0 -0
  15. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/versions/20260109T055839Z_476683af098d_initial_schema.py +0 -0
  16. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/versions/20260109T063747Z_2483bec70083_add_client_id.py +0 -0
  17. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/versions/20260205T120000Z_a1b2c3d4e5f6_add_max_concurrent.py +0 -0
  18. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/versions/20260205T180000Z_b7c8d9e0f1a2_add_periodic_key.py +0 -0
  19. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/migrations/versions/__init__.py +0 -0
  20. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/priority.py +0 -0
  21. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/registry.py +0 -0
  22. {python_pq-0.5.1 → python_pq-0.5.2}/src/pq/serialization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: python-pq
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Postgres-backed job queue for Python
5
5
  Author: ricwo
6
6
  Author-email: ricwo <r@cogram.com>
@@ -166,6 +166,20 @@ pq.schedule(fast_idempotent_task, run_every=timedelta(seconds=30), max_concurren
166
166
 
167
167
  The lock auto-expires after `max_runtime` seconds (or 1 hour by default) for crash safety.
168
168
 
169
+ ### Pausing & Resuming
170
+
171
+ Disable a periodic task without removing it:
172
+
173
+ ```python
174
+ # Pause - task stays in the database but won't run
175
+ pq.schedule(sync_inventory, run_every=timedelta(minutes=5), active=False)
176
+
177
+ # Resume
178
+ pq.schedule(sync_inventory, run_every=timedelta(minutes=5), active=True)
179
+ ```
180
+
181
+ New schedules are active by default.
182
+
169
183
  ### Multiple Schedules (Key)
170
184
 
171
185
  Use `key` to register the same function multiple times with different configurations:
@@ -145,6 +145,20 @@ pq.schedule(fast_idempotent_task, run_every=timedelta(seconds=30), max_concurren
145
145
 
146
146
  The lock auto-expires after `max_runtime` seconds (or 1 hour by default) for crash safety.
147
147
 
148
+ ### Pausing & Resuming
149
+
150
+ Disable a periodic task without removing it:
151
+
152
+ ```python
153
+ # Pause - task stays in the database but won't run
154
+ pq.schedule(sync_inventory, run_every=timedelta(minutes=5), active=False)
155
+
156
+ # Resume
157
+ pq.schedule(sync_inventory, run_every=timedelta(minutes=5), active=True)
158
+ ```
159
+
160
+ New schedules are active by default.
161
+
148
162
  ### Multiple Schedules (Key)
149
163
 
150
164
  Use `key` to register the same function multiple times with different configurations:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "python-pq"
3
- version = "0.5.1"
3
+ version = "0.5.2"
4
4
  description = "Postgres-backed job queue for Python"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -7,7 +7,7 @@ from pq.models import Periodic, Task, TaskStatus
7
7
  from pq.priority import Priority
8
8
  from pq.worker import PostExecuteHook, PreExecuteHook, TaskTimeoutError
9
9
 
10
- __version__ = "0.5.1"
10
+ __version__ = "0.5.2"
11
11
 
12
12
  __all__ = [
13
13
  "PQ",
@@ -237,6 +237,7 @@ class PQ:
237
237
  client_id: str | None = None,
238
238
  max_concurrent: int | None = 1,
239
239
  key: str = "",
240
+ active: bool = True,
240
241
  **kwargs: Any,
241
242
  ) -> int:
242
243
  """Schedule a periodic task.
@@ -256,6 +257,8 @@ class PQ:
256
257
  for future use and raise ValueError.
257
258
  key: Discriminator for multiple schedules of the same function.
258
259
  Defaults to "" (empty string).
260
+ active: Whether the task is active. Inactive tasks are not executed.
261
+ Defaults to True.
259
262
  **kwargs: Keyword arguments to pass to the handler.
260
263
 
261
264
  Returns:
@@ -316,6 +319,7 @@ class PQ:
316
319
  next_run=next_run,
317
320
  client_id=client_id,
318
321
  max_concurrent=max_concurrent,
322
+ active=active,
319
323
  )
320
324
  .on_conflict_do_update(
321
325
  index_elements=["name", "key"],
@@ -326,6 +330,7 @@ class PQ:
326
330
  "cron": cron_expr,
327
331
  "next_run": next_run,
328
332
  "max_concurrent": max_concurrent,
333
+ "active": active,
329
334
  },
330
335
  )
331
336
  .returning(Periodic.id)
@@ -0,0 +1,32 @@
1
+ """add periodic active
2
+
3
+ Revision ID: c3d4e5f6a7b8
4
+ Revises: b7c8d9e0f1a2
5
+ Create Date: 2026-02-17 12:00:00 Z
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = "c3d4e5f6a7b8"
17
+ down_revision: Union[str, Sequence[str], None] = "b7c8d9e0f1a2"
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ """Add active column to pq_periodic."""
24
+ op.add_column(
25
+ "pq_periodic",
26
+ sa.Column("active", sa.Boolean(), nullable=False, server_default="true"),
27
+ )
28
+
29
+
30
+ def downgrade() -> None:
31
+ """Remove active column from pq_periodic."""
32
+ op.drop_column("pq_periodic", "active")
@@ -6,6 +6,7 @@ from typing import Any
6
6
 
7
7
  from sqlalchemy import (
8
8
  BigInteger,
9
+ Boolean,
9
10
  DateTime,
10
11
  Enum,
11
12
  Identity,
@@ -92,6 +93,7 @@ class Periodic(Base):
92
93
  cron: Mapped[str | None] = mapped_column(String(100), nullable=True)
93
94
  next_run: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
94
95
  max_concurrent: Mapped[int | None] = mapped_column(SmallInteger, nullable=True)
96
+ active: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default="true")
95
97
  last_run: Mapped[datetime | None] = mapped_column(
96
98
  DateTime(timezone=True), nullable=True
97
99
  )
@@ -560,6 +560,7 @@ def _process_periodic_task(
560
560
  # Claim highest priority due periodic task with FOR UPDATE SKIP LOCKED
561
561
  # Filter out tasks that are locked (max_concurrent=1 and locked_until in future)
562
562
  stmt = select(Periodic).where(
563
+ Periodic.active.is_(True),
563
564
  Periodic.next_run <= func.now(),
564
565
  or_(
565
566
  Periodic.max_concurrent.is_(None),
File without changes
File without changes
File without changes
File without changes