diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..5d93a6f
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,6 @@
+# https://docs.docker.com/engine/reference/builder/#dockerignore-file
+*
+!django_outbox_pattern
+!scripts
+!pyproject.toml
+!poetry.lock
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..8813207
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,3 @@
+/.github @juntossomosmais/core
+* @juntossomosmais/loyalty-backends
+* @juntossomosmais/loja-virtual-backends
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..b3b393b
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,22 @@
+version: 2
+updates:
+ - package-ecosystem: "pip"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ time: "08:00"
+ day: "sunday"
+ target-branch: "main"
+ reviewers:
+ - "juntossomosmais/loyalty"
+ - "juntossomosmais/loja-virtual"
+ - package-ecosystem: "docker"
+ directory: "/"
+ schedule:
+ interval: "monthly"
+ time: "08:00"
+ day: "sunday"
+ target-branch: "main"
+ reviewers:
+ - "juntossomosmais/loyalty"
+ - "juntossomosmais/loja-virtual"
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..096825c
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,24 @@
+# Infos
+
+## [Task name on sprint board](https://juntossomosmais.monday.com/boards/)
+
+### What is being delivered?
+
+- Describe here all the changes that will be made and what the expected result is.
+
+### What impacts?
+
+- Describe what impacts this delivery has and whether it can cause side effects in other parts of the application.
+
+### Reversal plan
+
+- Describe which plan we should follow if this delivery has to be reversed.
+Ex.: Revert applied migrations, revert commits, redeploy the application via Azure DevOps
+
+### Where to monitor
+
+- Describe how this delivery can be monitored and how to analyze the data to ensure it works.
+
+- Ex.: [Specific Kibana query name]()
+
+
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e2fd586..7de7d66 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -8,16 +8,26 @@ repos:
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- - repo: https://github.com/pycqa/pylint
- rev: v3.2.6
- hooks:
- - id: pylint
- additional_dependencies: [ django, 'stomp.py' ]
- exclude: migrations
- repo: https://github.com/pycqa/flake8
rev: 7.1.1
hooks:
- id: flake8
+ additional_dependencies: [
+ 'flake8-bugbear',
+ 'flake8-comprehensions',
+ 'flake8-debugger',
+ ]
+ - repo: https://github.com/pycqa/autoflake
+ rev: v2.3.1
+ hooks:
+ - id: autoflake
+ args:
+ [
+ "--in-place",
+ "--remove-all-unused-imports",
+ "--recursive",
+ "--ignore-init-module-imports",
+ ]
- repo: https://github.com/psf/black
rev: 24.8.0
hooks:
@@ -27,3 +37,11 @@ repos:
rev: 5.13.2
hooks:
- id: isort
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v1.11.2
+ hooks:
+ - id: mypy
+ additional_dependencies:
+ ["types-all", "types-pkg-resources==0.1.3"]
+ pass_filenames: false
+ args: ['.']
diff --git a/django_outbox_pattern/bases.py b/django_outbox_pattern/bases.py
index c045a94..eb334cb 100644
--- a/django_outbox_pattern/bases.py
+++ b/django_outbox_pattern/bases.py
@@ -11,7 +11,7 @@
class Base:
def __init__(self, connection, username, passcode):
- self._credentials = dict(username=username, passcode=passcode) # pylint: disable=R1735
+ self._credentials = {"username": username, "passcode": passcode}
self.attempts = 0
self.connection = connection
diff --git a/django_outbox_pattern/consumers.py b/django_outbox_pattern/consumers.py
index cda31bf..47b45d4 100644
--- a/django_outbox_pattern/consumers.py
+++ b/django_outbox_pattern/consumers.py
@@ -1,6 +1,6 @@
-# pylint: disable=R0902
import json
import logging
+
from datetime import timedelta
from django import db
@@ -68,7 +68,7 @@ def message_handler(self, body, headers):
message_id,
)
- except BaseException: # pylint: disable=broad-exception-caught
+ except Exception:
logger.exception("An exception has been caught during callback processing flow")
payload.nack()
@@ -107,7 +107,6 @@ def _create_queue(self, destination, headers, queue_name=None):
self._subscribe(destination, self.subscribe_id, headers, queue_name)
def _subscribe(self, destination, subscribe_id, headers, queue_name=None, dlq=False):
- # pylint: disable=too-many-arguments
routing_key = destination.split("/")[-1]
queue_name = queue_name if queue_name else routing_key
if dlq:
diff --git a/django_outbox_pattern/decorators.py b/django_outbox_pattern/decorators.py
index 81d5984..0a31e6a 100644
--- a/django_outbox_pattern/decorators.py
+++ b/django_outbox_pattern/decorators.py
@@ -1,4 +1,5 @@
import json
+
from typing import List
from typing import NamedTuple
from typing import Optional
diff --git a/django_outbox_pattern/exceptions.py b/django_outbox_pattern/exceptions.py
index 9b39e2b..9341f4d 100644
--- a/django_outbox_pattern/exceptions.py
+++ b/django_outbox_pattern/exceptions.py
@@ -1,5 +1,5 @@
class ExceededSendAttemptsException(Exception):
"""Raised when the limit of attempts to send messages to the broker is exceeded"""
- def __init__(self, attempts): # pylint: disable=super-init-not-called
+ def __init__(self, attempts):
self.attempts = attempts
diff --git a/django_outbox_pattern/management/commands/publish.py b/django_outbox_pattern/management/commands/publish.py
index e8df6a0..6f86b4b 100644
--- a/django_outbox_pattern/management/commands/publish.py
+++ b/django_outbox_pattern/management/commands/publish.py
@@ -1,5 +1,6 @@
import logging
import sys
+
from datetime import timedelta
from time import sleep
diff --git a/django_outbox_pattern/management/commands/subscribe.py b/django_outbox_pattern/management/commands/subscribe.py
index 7c0c738..6f4bc74 100644
--- a/django_outbox_pattern/management/commands/subscribe.py
+++ b/django_outbox_pattern/management/commands/subscribe.py
@@ -1,4 +1,5 @@
import sys
+
from time import sleep
from django.core.management.base import BaseCommand
diff --git a/django_outbox_pattern/migrations/0006_published_published_status_27c9ec_btree.py b/django_outbox_pattern/migrations/0006_published_published_status_27c9ec_btree.py
index c6429b3..22fec41 100644
--- a/django_outbox_pattern/migrations/0006_published_published_status_27c9ec_btree.py
+++ b/django_outbox_pattern/migrations/0006_published_published_status_27c9ec_btree.py
@@ -1,6 +1,7 @@
# Generated by Django 4.2.14 on 2024-07-15 16:14
import django.contrib.postgres.indexes
+
from django.contrib.postgres.operations import AddIndexConcurrently
from django.db import migrations
diff --git a/django_outbox_pattern/models.py b/django_outbox_pattern/models.py
index a2cc6c8..4419410 100644
--- a/django_outbox_pattern/models.py
+++ b/django_outbox_pattern/models.py
@@ -1,4 +1,5 @@
import uuid
+
from datetime import timedelta
from django.contrib.postgres.indexes import BTreeIndex
diff --git a/django_outbox_pattern/producers.py b/django_outbox_pattern/producers.py
index 94977f9..b9cb78c 100644
--- a/django_outbox_pattern/producers.py
+++ b/django_outbox_pattern/producers.py
@@ -1,5 +1,6 @@
import json
import logging
+
from datetime import timedelta
from time import sleep
diff --git a/docker-compose.yml b/docker-compose.yml
index a34c38d..5b627be 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -57,7 +57,7 @@ services:
build: .
volumes:
- .:/app
- command: [ "./scripts/start-formatter-lint.sh" ]
+ command: ["./scripts/start-formatter-lint.sh"]
networks:
- djangooutboxpattern
diff --git a/poetry.lock b/poetry.lock
index 9c3f5ed..5bdd076 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -125,13 +125,13 @@ files = [
[[package]]
name = "django"
-version = "5.1"
+version = "5.1.1"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
files = [
- {file = "Django-5.1-py3-none-any.whl", hash = "sha256:d3b811bf5371a26def053d7ee42a9df1267ef7622323fe70a601936725aa4557"},
- {file = "Django-5.1.tar.gz", hash = "sha256:848a5980e8efb76eea70872fb0e4bc5e371619c70fffbe48e3e1b50b2c09455d"},
+ {file = "Django-5.1.1-py3-none-any.whl", hash = "sha256:71603f27dac22a6533fb38d83072eea9ddb4017fead6f67f2562a40402d61c3f"},
+ {file = "Django-5.1.1.tar.gz", hash = "sha256:021ffb7fdab3d2d388bc8c7c2434eb9c1f6f4d09e6119010bbb1694dda286bc2"},
]
[package.dependencies]
@@ -261,6 +261,7 @@ files = [
{file = "psycopg2_binary-2.9.9-cp311-cp311-win32.whl", hash = "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d"},
{file = "psycopg2_binary-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417"},
{file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d"},
{file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212"},
{file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493"},
{file = "psycopg2_binary-2.9.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996"},
@@ -269,6 +270,8 @@ files = [
{file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07"},
{file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb"},
{file = "psycopg2_binary-2.9.9-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-win32.whl", hash = "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93"},
+ {file = "psycopg2_binary-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab"},
{file = "psycopg2_binary-2.9.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a"},
{file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9"},
{file = "psycopg2_binary-2.9.9-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77"},
@@ -459,4 +462,4 @@ test = ["websockets"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<4.0"
-content-hash = "677528d9fb8dcd499fbb72160669d5f42523939b59c7f3230e2f08a0f6a15b0d"
+content-hash = "c76cd77b1fecefd82f19f64bb75933c57c1c003ad4aac49b99d49ad56ed7d1ad"
diff --git a/pyproject.toml b/pyproject.toml
index 558e7ce..7ba36c4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "django-outbox-pattern"
-version = "2.0.0"
+version = "2.1.0"
description = "A django application to make it easier to use the transactional outbox pattern"
license = "MIT"
authors = ["Hugo Brilhante "]
@@ -36,7 +36,7 @@ django = ">=5.0.8"
[tool.poetry.group.dev.dependencies]
psycopg2-binary = "^2.9.6"
coverage = "*"
-pre-commit = "*"
+pre-commit = "^3.8.0"
[tool.black]
line-length = 120
@@ -59,33 +59,53 @@ exclude = '''
)
'''
[tool.isort]
-multi_line_output = 3
-include_trailing_comma = 'True'
-force_grid_wrap = 0
force_single_line = true
-use_parentheses = 'True'
-ensure_newline_before_comments = 'True'
line_length = 120
+py_version = 312
+use_parentheses = true
+multi_line_output = 3
+include_trailing_comma = true
+lines_between_types = 1
+sections = ["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
-[tool.pylint.master]
-ignore = []
+[tool.mypy]
+python_version = "3.10"
-[tool.pylint.messages_control]
-disable = [
- "no-member",
- "missing-class-docstring",
- "missing-function-docstring",
- "missing-module-docstring",
- "too-few-public-methods",
- "logging-not-lazy",
- "consider-using-f-string",
- "logging-fstring-interpolation",
- "too-many-ancestors"
-]
+# flake8-mypy expects the two following for sensible formatting
+show_column_numbers = true
+show_error_context = false
+
+# do not follow imports (except for ones found in typeshed)
+follow_imports = "skip"
+
+# suppress errors about unsatisfied imports
+ignore_missing_imports = true
+
+# allow untyped calls as a consequence of the options above
+disallow_untyped_calls = false
+
+# allow returning Any as a consequence of the options above
+warn_return_any = false
+
+# treat Optional per PEP 484
+strict_optional = true
+
+# ensure all execution paths are returning
+warn_no_return = true
+
+# lint-style cleanliness for typing needs to be disabled; returns more errors
+# than the full run.
+warn_redundant_casts = false
+warn_unused_ignores = false
-[tool.pylint.format]
-max-line-length = "120"
+# The following are off by default since they're too noisy.
+# Flip them on if you feel adventurous.
+disallow_untyped_defs = false
+check_untyped_defs = false
+[[tool.mypy.overrides]]
+module = "*.migrations.*"
+ignore_errors = true
[build-system]
requires = ["poetry-core>=1.0.0"]
diff --git a/scripts/start-formatter-lint.sh b/scripts/start-formatter-lint.sh
index f8dd2c3..36a4cb5 100755
--- a/scripts/start-formatter-lint.sh
+++ b/scripts/start-formatter-lint.sh
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
-
+set -e
git config --global --add safe.directory /app
pre-commit run --all-files
diff --git a/tests/integration/test_consumer.py b/tests/integration/test_consumer.py
index 4756172..161a1aa 100644
--- a/tests/integration/test_consumer.py
+++ b/tests/integration/test_consumer.py
@@ -13,7 +13,7 @@
def get_callback(raise_except=False):
- def callback(payload): # pylint: disable=unused-argument
+ def callback(payload):
if raise_except:
raise KeyError("Test exception")
payload.save()
@@ -113,10 +113,10 @@ def test_consumer_message_should_remove_old_messages(self):
def test_consumer_should_not_remove_old_message_when_cache_exists(self):
self._create_message_in_the_past(35, 1)
- self.consumer._remove_old_messages() # pylint: disable=W0212
+ self.consumer._remove_old_messages()
self.assertFalse(self.consumer.received_class.objects.filter(msg_id=1).exists())
self._create_message_in_the_past(35, 2)
- self.consumer._remove_old_messages() # pylint: disable=W0212
+ self.consumer._remove_old_messages()
self.assertTrue(self.consumer.received_class.objects.filter(msg_id=2).exists())
def _create_message_in_the_past(self, day_ago, msg_id):
@@ -133,7 +133,7 @@ def test_consumer_should_consume_message_event_if_a_exception_happen_in_cache(se
destination = "/topic/consumer.v5"
callback = get_callback()
self.consumer.start(callback, destination)
- with self.assertRaises(Exception):
+ with self.assertRaises(Exception): # noqa
self.consumer.connection.send(destination=destination, body='{"message": "Message test no raise"}')
sleep(1)
self.listener.get_latest_message()
diff --git a/tests/integration/test_producer.py b/tests/integration/test_producer.py
index e2722d8..0312969 100644
--- a/tests/integration/test_producer.py
+++ b/tests/integration/test_producer.py
@@ -1,4 +1,5 @@
import json
+
from datetime import datetime
from datetime import timedelta
from unittest import mock
@@ -112,10 +113,10 @@ def test_producer_should_remove_old_messages(self):
def test_consumer_should_not_remove_old_message_when_cache_exists(self):
message = self._create_message_in_the_past(31)
producer = factory_producer()
- producer._remove_old_messages() # pylint: disable=W0212
+ producer._remove_old_messages()
self.assertFalse(Published.objects.filter(id=message.id).exists())
message1 = self._create_message_in_the_past(31)
- producer._remove_old_messages() # pylint: disable=W0212
+ producer._remove_old_messages()
self.assertTrue(Published.objects.filter(id=message1.id).exists())
@mock.patch("django_outbox_pattern.producers.cache")
@@ -125,7 +126,7 @@ def test_producer_should_send_message_event_if_a_exception_happen_in_cache(self,
producer.set_listener("test_listener", TestListener(print_to_log=True))
producer.connection.subscribe(destination=self.fake_destination, id=1)
listener = producer.get_listener("test_listener")
- with self.assertRaises(Exception):
+ with self.assertRaisesRegex(Exception, "Cache get failed"):
producer.send_event(destination=self.fake_destination, body={"message": "fake message body"})
listener.wait_for_message()
diff --git a/tests/integration/test_publish_command.py b/tests/integration/test_publish_command.py
index 61ce139..7389242 100644
--- a/tests/integration/test_publish_command.py
+++ b/tests/integration/test_publish_command.py
@@ -1,4 +1,3 @@
-# pylint: disable=R0801
from io import StringIO
from unittest.mock import MagicMock
from unittest.mock import PropertyMock
diff --git a/tests/unit/test_consumer.py b/tests/unit/test_consumer.py
index bb94802..c33cf29 100644
--- a/tests/unit/test_consumer.py
+++ b/tests/unit/test_consumer.py
@@ -44,7 +44,7 @@ def test_consumer_message_handler_should_not_save_message_when_have_a_exception_
def callback(payload: Payload):
with transaction.atomic():
payload.save()
- raise StompException("mocked") # pylint: disable=broad-exception-raised
+ raise StompException("mocked")
with self.assertLogs(level="ERROR") as log:
self.consumer.callback = callback
diff --git a/tests/unit/test_publish_command.py b/tests/unit/test_publish_command.py
index 95af3a1..699e26e 100644
--- a/tests/unit/test_publish_command.py
+++ b/tests/unit/test_publish_command.py
@@ -1,4 +1,3 @@
-# pylint: disable=R0801
from io import StringIO
from unittest.mock import MagicMock
from unittest.mock import PropertyMock