diff --git a/.copier-answers.yml b/.copier-answers.yml
index ec696d9846..3822793f07 100644
--- a/.copier-answers.yml
+++ b/.copier-answers.yml
@@ -1,5 +1,5 @@
# Do NOT update manually; changes here will be overwritten by Copier
-_commit: v1.29
+_commit: v1.38
_src_path: gh:oca/oca-addons-repo-template
ci: GitHub
convert_readme_fragments_to_markdown: false
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..e0d56685a9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+test-requirements.txt merge=union
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index afd7524ef0..5e6c3fb439 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -17,6 +17,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.11"
+ cache: 'pip'
- name: Get python version
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV
- uses: actions/cache@v4
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 5d0f95283c..5ec4eb783e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -42,7 +42,7 @@ jobs:
makepot: "true"
services:
postgres:
- image: postgres:12.0
+ image: postgres:12
env:
POSTGRES_USER: odoo
POSTGRES_PASSWORD: odoo
@@ -63,6 +63,13 @@ jobs:
run: oca_init_test_database
- name: Run tests
run: oca_run_tests
+ - name: Upload screenshots from JS tests
+ uses: actions/upload-artifact@v4
+ if: ${{ failure() }}
+ with:
+ name: Screenshots of failed JS tests - ${{ matrix.name }}${{ join(matrix.include) }}
+ path: /tmp/odoo_tests/${{ env.PGDATABASE }}
+ if-no-files-found: ignore
- uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index cc8df91bb0..205d549a2f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -40,7 +40,7 @@ repos:
language: fail
files: '[a-zA-Z0-9_]*/i18n/en\.po$'
- repo: https://github.com/oca/maintainer-tools
- rev: d5fab7ee87fceee858a3d01048c78a548974d935
+ rev: f9b919b9868143135a9c9cb03021089cabba8223
hooks:
# update the NOT INSTALLABLE ADDONS section above
- id: oca-update-pre-commit-excluded-addons
@@ -105,6 +105,7 @@ repos:
additional_dependencies:
- "eslint@8.24.0"
- "eslint-plugin-jsdoc@"
+ - "globals@"
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
@@ -141,7 +142,7 @@ repos:
- --settings=.
exclude: /__init__\.py$
- repo: https://github.com/acsone/setuptools-odoo
- rev: 3.1.8
+ rev: 3.3.2
hooks:
- id: setuptools-odoo-make-default
- id: setuptools-odoo-get-requirements
diff --git a/.pylintrc b/.pylintrc
index 554913276b..0a521c31ff 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -25,19 +25,25 @@ disable=all
enable=anomalous-backslash-in-string,
api-one-deprecated,
api-one-multi-together,
- assignment-from-none,
- attribute-deprecated,
class-camelcase,
- dangerous-default-value,
dangerous-view-replace-wo-priority,
- development-status-allowed,
duplicate-id-csv,
- duplicate-key,
duplicate-xml-fields,
duplicate-xml-record-id,
eval-referenced,
- eval-used,
incoherent-interpreter-exec-perm,
+ openerp-exception-warning,
+ redundant-modulename-xml,
+ relative-import,
+ rst-syntax-error,
+ wrong-tabs-instead-of-spaces,
+ xml-syntax-error,
+ assignment-from-none,
+ attribute-deprecated,
+ dangerous-default-value,
+ development-status-allowed,
+ duplicate-key,
+ eval-used,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
@@ -48,73 +54,68 @@ enable=anomalous-backslash-in-string,
method-inverse,
method-required-super,
method-search,
- openerp-exception-warning,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
- redundant-modulename-xml,
reimported,
- relative-import,
return-in-init,
- rst-syntax-error,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
- wrong-tabs-instead-of-spaces,
- xml-syntax-error,
- attribute-string-redundant,
character-not-valid-in-resource-link,
- consider-merging-classes-inherited,
- context-overridden,
create-user-wo-reset-password,
dangerous-filter-wo-user,
dangerous-qweb-replace-wo-priority,
deprecated-data-xml-node,
deprecated-openerp-xml-node,
duplicate-po-message-definition,
- except-pass,
file-not-used,
+ missing-newline-extrafiles,
+ old-api7-method-defined,
+ po-msgstr-variables,
+ po-syntax-error,
+ str-format-used,
+ unnecessary-utf8-coding-comment,
+ xml-attribute-translatable,
+ xml-deprecated-qweb-directive,
+ xml-deprecated-tree-attribute,
+ attribute-string-redundant,
+ consider-merging-classes-inherited,
+ context-overridden,
+ except-pass,
invalid-commit,
manifest-maintainers-list,
- missing-newline-extrafiles,
missing-readme,
missing-return,
odoo-addons-relative-import,
- old-api7-method-defined,
- po-msgstr-variables,
- po-syntax-error,
renamed-field-parameter,
resource-not-exist,
- str-format-used,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
- unnecessary-utf8-coding-comment,
website-manifest-key-not-valid-uri,
- xml-attribute-translatable,
- xml-deprecated-qweb-directive,
- xml-deprecated-tree-attribute,
external-request-timeout,
- # messages that do not cause the lint step to fail
- consider-merging-classes-inherited,
+ missing-manifest-dependency,
+ too-complex,,
create-user-wo-reset-password,
dangerous-filter-wo-user,
- deprecated-module,
file-not-used,
- invalid-commit,
- missing-manifest-dependency,
missing-newline-extrafiles,
- missing-readme,
no-utf8-coding-comment,
- odoo-addons-relative-import,
old-api7-method-defined,
+ unnecessary-utf8-coding-comment,
+ # messages that do not cause the lint step to fail
+ consider-merging-classes-inherited,
+ deprecated-module,
+ invalid-commit,
+ missing-readme,
+ odoo-addons-relative-import,
redefined-builtin,
- too-complex,
- unnecessary-utf8-coding-comment
+ manifest-external-assets
[REPORTS]
diff --git a/.pylintrc-mandatory b/.pylintrc-mandatory
index 7a0cd4efef..098393aadb 100644
--- a/.pylintrc-mandatory
+++ b/.pylintrc-mandatory
@@ -17,19 +17,25 @@ disable=all
enable=anomalous-backslash-in-string,
api-one-deprecated,
api-one-multi-together,
- assignment-from-none,
- attribute-deprecated,
class-camelcase,
- dangerous-default-value,
dangerous-view-replace-wo-priority,
- development-status-allowed,
duplicate-id-csv,
- duplicate-key,
duplicate-xml-fields,
duplicate-xml-record-id,
eval-referenced,
- eval-used,
incoherent-interpreter-exec-perm,
+ openerp-exception-warning,
+ redundant-modulename-xml,
+ relative-import,
+ rst-syntax-error,
+ wrong-tabs-instead-of-spaces,
+ xml-syntax-error,
+ assignment-from-none,
+ attribute-deprecated,
+ dangerous-default-value,
+ development-status-allowed,
+ duplicate-key,
+ eval-used,
license-allowed,
manifest-author-string,
manifest-deprecated-key,
@@ -40,56 +46,50 @@ enable=anomalous-backslash-in-string,
method-inverse,
method-required-super,
method-search,
- openerp-exception-warning,
pointless-statement,
pointless-string-statement,
print-used,
redundant-keyword-arg,
- redundant-modulename-xml,
reimported,
- relative-import,
return-in-init,
- rst-syntax-error,
sql-injection,
too-few-format-args,
translation-field,
translation-required,
unreachable,
use-vim-comment,
- wrong-tabs-instead-of-spaces,
- xml-syntax-error,
- attribute-string-redundant,
character-not-valid-in-resource-link,
- consider-merging-classes-inherited,
- context-overridden,
create-user-wo-reset-password,
dangerous-filter-wo-user,
dangerous-qweb-replace-wo-priority,
deprecated-data-xml-node,
deprecated-openerp-xml-node,
duplicate-po-message-definition,
- except-pass,
file-not-used,
+ missing-newline-extrafiles,
+ old-api7-method-defined,
+ po-msgstr-variables,
+ po-syntax-error,
+ str-format-used,
+ unnecessary-utf8-coding-comment,
+ xml-attribute-translatable,
+ xml-deprecated-qweb-directive,
+ xml-deprecated-tree-attribute,
+ attribute-string-redundant,
+ consider-merging-classes-inherited,
+ context-overridden,
+ except-pass,
invalid-commit,
manifest-maintainers-list,
- missing-newline-extrafiles,
missing-readme,
missing-return,
odoo-addons-relative-import,
- old-api7-method-defined,
- po-msgstr-variables,
- po-syntax-error,
renamed-field-parameter,
resource-not-exist,
- str-format-used,
test-folder-imported,
translation-contains-variable,
translation-positional-used,
- unnecessary-utf8-coding-comment,
website-manifest-key-not-valid-uri,
- xml-attribute-translatable,
- xml-deprecated-qweb-directive,
- xml-deprecated-tree-attribute,
external-request-timeout
[REPORTS]
diff --git a/README.md b/README.md
index 2a8307ea88..aeafe8e05c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,7 @@
+[](https://odoo-community.org/get-involved?utm_source=repo-readme)
+
+# Queue Job
[](https://runboat.odoo-community.org/builds?repo=OCA/queue&target_branch=16.0)
[](https://github.com/OCA/queue/actions/workflows/pre-commit.yml?query=branch%3A16.0)
[](https://github.com/OCA/queue/actions/workflows/test.yml?query=branch%3A16.0)
@@ -7,8 +10,6 @@
-# Odoo Queue Job
-
Asynchronous Job Queue. Delay Model methods in asynchronous jobs, executed in the background as soon as possible or on a schedule. Support Channels to segregates jobs in different queues with different capacities. Unlike scheduled tasks, a job captures arguments for later processing.
@@ -23,7 +24,7 @@ addon | version | maintainers | summary
--- | --- | --- | ---
[base_export_async](base_export_async/) | 16.0.1.2.0 | | Asynchronous export with job queue
[base_import_async](base_import_async/) | 16.0.1.2.1 | | Import CSV files in the background
-[queue_job](queue_job/) | 16.0.2.13.1 |
| Job Queue
+[queue_job](queue_job/) | 16.0.2.13.2 |
| Job Queue
[queue_job_batch](queue_job_batch/) | 16.0.1.0.1 | | Job Queue Batch
[queue_job_cron](queue_job_cron/) | 16.0.2.1.0 | | Scheduled Actions as Queue Jobs
[queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 16.0.1.1.0 |
| Run jobs without a dedicated JobRunner
diff --git a/queue_job/README.rst b/queue_job/README.rst
index 89863f1a1e..1adbad629f 100644
--- a/queue_job/README.rst
+++ b/queue_job/README.rst
@@ -11,7 +11,7 @@ Job Queue
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:10dc03f2537877e01294e7b905641edb2d06a2e9f32d4adfc4c4c07224a40ea9
+ !! source digest: sha256:2bd5c794474dd35dcd9b22179fa7d2690e791192862beaeeb674d8bedbb77558
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png
diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py
index e4de37cd33..158c54b1de 100644
--- a/queue_job/__manifest__.py
+++ b/queue_job/__manifest__.py
@@ -2,7 +2,7 @@
{
"name": "Job Queue",
- "version": "16.0.2.13.1",
+ "version": "16.0.2.13.2",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
diff --git a/queue_job/controllers/main.py b/queue_job/controllers/main.py
index 51f91794b8..e3dbd5309c 100644
--- a/queue_job/controllers/main.py
+++ b/queue_job/controllers/main.py
@@ -6,6 +6,7 @@
import random
import time
import traceback
+from contextlib import contextmanager
from io import StringIO
from typing import Optional
@@ -26,6 +27,29 @@
DEPENDS_MAX_TRIES_ON_CONCURRENCY_FAILURE = 5
+@contextmanager
+def _prevent_commit(cr):
+ """Context manager to prevent commits on a cursor.
+
+ Commiting while the job is not finished would release the job lock, causing
+ it to be started again by the dead jobs requeuer.
+ """
+
+ def forbidden_commit(*args, **kwargs):
+ raise RuntimeError(
+ "Commit is forbidden in queue jobs. "
+ "If the current job is a cron running as queue job, "
+ "modify it to run as a normal cron."
+ )
+
+ original_commit = cr.commit
+ cr.commit = forbidden_commit
+ try:
+ yield
+ finally:
+ cr.commit = original_commit
+
+
class RunJobController(http.Controller):
@classmethod
def _acquire_job(cls, env: api.Environment, job_uuid: str) -> Optional[Job]:
@@ -69,13 +93,16 @@ def _acquire_job(cls, env: api.Environment, job_uuid: str) -> Optional[Job]:
def _try_perform_job(cls, env, job):
"""Try to perform the job, mark it done and commit if successful."""
_logger.debug("%s started", job)
- job.perform()
- # Triggers any stored computed fields before calling 'set_done'
- # so that will be part of the 'exec_time'
- env.flush_all()
- job.set_done()
- job.store()
- env.flush_all()
+ # TODO refactor, the relation between env and job.env is not clear
+ assert env.cr is job.env.cr
+ with _prevent_commit(env.cr):
+ job.perform()
+ # Triggers any stored computed fields before calling 'set_done'
+ # so that will be part of the 'exec_time'
+ env.flush_all()
+ job.set_done()
+ job.store()
+ env.flush_all()
env.cr.commit()
_logger.debug("%s done", job)
@@ -212,6 +239,7 @@ def create_test_job(
size=1,
failure_rate=0,
job_duration=0,
+ commit_within_job=False,
):
"""Create test jobs
@@ -267,6 +295,7 @@ def create_test_job(
description=description,
failure_rate=failure_rate,
job_duration=job_duration,
+ commit_within_job=commit_within_job,
)
if size > 1:
@@ -278,6 +307,7 @@ def create_test_job(
description=description,
failure_rate=failure_rate,
job_duration=job_duration,
+ commit_within_job=commit_within_job,
)
return ""
@@ -290,6 +320,7 @@ def _create_single_test_job(
size=1,
failure_rate=0,
job_duration=0,
+ commit_within_job=False,
):
delayed = (
http.request.env["queue.job"]
@@ -299,7 +330,11 @@ def _create_single_test_job(
channel=channel,
description=description,
)
- ._test_job(failure_rate=failure_rate, job_duration=job_duration)
+ ._test_job(
+ failure_rate=failure_rate,
+ job_duration=job_duration,
+ commit_within_job=commit_within_job,
+ )
)
return "job uuid: %s" % (delayed.db_record().uuid,)
@@ -314,6 +349,7 @@ def _create_graph_test_jobs(
description="Test job",
failure_rate=0,
job_duration=0,
+ commit_within_job=False,
):
model = http.request.env["queue.job"]
current_count = 0
@@ -336,7 +372,11 @@ def _create_graph_test_jobs(
max_retries=max_retries,
channel=channel,
description="%s #%d" % (description, current_count),
- )._test_job(failure_rate=failure_rate, job_duration=job_duration)
+ )._test_job(
+ failure_rate=failure_rate,
+ job_duration=job_duration,
+ commit_within_job=commit_within_job,
+ )
)
grouping = random.choice(possible_grouping_methods)
diff --git a/queue_job/models/queue_job.py b/queue_job/models/queue_job.py
index d538a2a75c..f8c117fa2d 100644
--- a/queue_job/models/queue_job.py
+++ b/queue_job/models/queue_job.py
@@ -459,9 +459,11 @@ def related_action_open_record(self):
)
return action
- def _test_job(self, failure_rate=0, job_duration=0):
+ def _test_job(self, failure_rate=0, job_duration=0, commit_within_job=False):
_logger.info("Running test job.")
if random.random() <= failure_rate:
raise JobError("Job failed")
if job_duration:
time.sleep(job_duration)
+ if commit_within_job:
+ self.env.cr.commit() # pylint: disable=invalid-commit
diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html
index de0d4f22cd..bc065039fc 100644
--- a/queue_job/static/description/index.html
+++ b/queue_job/static/description/index.html
@@ -372,7 +372,7 @@
This addon adds an integrated Job Queue to Odoo.