diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..981833b --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,5 @@ +[settings] +profile=black +line_length=100 +atomic=true +skip=migrations diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ecf9a4a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +fail_fast: true +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.2.0 + hooks: + - id: trailing-whitespace + - id: debug-statements + - id: check-added-large-files + stages: [commit] + +- repo: https://github.com/commitizen-tools/commitizen + rev: v2.24.0 + hooks: + - id: commitizen + stages: [commit-msg] + +- repo: https://github.com/pre-commit/mirrors-yapf + rev: v0.32.0 + hooks: + - id: yapf + stages: [commit] + args: + - --parallel + - --in-place + exclude: ^.*\b(migrations)\b.*$ + +- repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + stages: [commit] + +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.942 + hooks: + - id: mypy + additional_dependencies: [ + django-stubs, + types-freezegun, + types-python-dateutil, + ] + stages: [commit] diff --git a/.prospector.yml b/.prospector.yml index c032c01..7e91956 100644 --- a/.prospector.yml +++ b/.prospector.yml @@ -1,4 +1,4 @@ -strictness: medium +strictness: high test-warnings: true doc-warnings: false autodetect: true diff --git a/.pylintrc b/.pylintrc index 25d1095..3af2655 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,7 +1,7 @@ [MASTER] profile=no persistent=yes -ignore=migrations__init__.py +ignore=migrations,__init__.py cache-size=500 [MESSAGES CONTROL] @@ -20,7 +20,7 @@ cache-size=500 # E1101 Instace XX has no YY member (Django create members on the fly) # E1103 Instace of list has no YY member (Django create members on the fly) # E1120 Weird behavior with class based views -disable=C0111,C1001,I0011,I0012,W0704,W0142,W0212,W0201,W0232,W0613,W0702,R0201,R0801,R0924,E1101,E1002,E1103,E1120,C0330,R0901 +disable=C0111,C1001,I0011,I0012,W0704,W0142,W0212,W0201,W0232,W0613,W0702,R0201,R0801,R0924,E1101,E1002,E1103,E1120,C0330,R0901,C0415 [REPORTS] output-format=parseable diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..9c0d2dd --- /dev/null +++ b/.style.yapf @@ -0,0 +1,14 @@ +[style] +based_on_style = pep8 +dedent_closing_brackets = true +coalesce_brackets = false +allow_split_before_dict_value = false +column_limit = 100 +split_before_dot = false +split_all_top_level_comma_separated_values = true +spaces_around_tuple_delimiters = false +space_between_ending_comma_and_closing_bracket = false +blank_line_before_nested_class_or_def = false +align_closing_bracket_with_visual_indent = true +split_before_first_argument = false +split_before_expression_after_opening_paren = true diff --git a/.yapfignore b/.yapfignore new file mode 100644 index 0000000..e69de29 diff --git a/ax3_model_extras/validators.py b/ax3_model_extras/validators.py index f2749fb..a9ea9e5 100644 --- a/ax3_model_extras/validators.py +++ b/ax3_model_extras/validators.py @@ -1,5 +1,8 @@ import magic +import phonenumbers +from django.core.exceptions import ValidationError from django.core.validators import BaseValidator +from django.utils.deconstruct import deconstructible from django.utils.translation import gettext_lazy as _ @@ -60,3 +63,33 @@ def compare(self, a, b): return mime not in b except AttributeError: return True + + +@deconstructible +class PhoneNumberValidator: + message = _('Invalid phone number.') + code = 'invalid_phone_number' + + def __init__(self, iso_code, message=None, code=None): + self.iso_code = iso_code + + if message is not None: + self.message = message + if code is not None: + self.code = code + + def __call__(self, value): + try: + mobile_number = phonenumbers.parse(str(value), self.iso_code) + + if not phonenumbers.is_valid_number(mobile_number): + raise ValidationError(self.message, code=self.code) + + except phonenumbers.NumberParseException as exc: + raise ValidationError(str(exc), code=self.code) from exc + + def __eq__(self, other): + return ( + isinstance(other, self.__class__) and self.iso_code == other.iso_code + and self.message == other.message and self.code == other.code + ) diff --git a/ax3_model_extras/webp.py b/ax3_model_extras/webp.py index a6cd303..3bfb921 100644 --- a/ax3_model_extras/webp.py +++ b/ax3_model_extras/webp.py @@ -2,7 +2,6 @@ import subprocess from django.core.files.base import ContentFile - from PIL import Image diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..1600c07 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,11 @@ +[mypy] +python_version = 3.8 +check_untyped_defs = True +ignore_missing_imports = True +show_error_codes = True +disable_error_code = attr-defined,union-attr,misc +warn_unused_ignores = True +warn_redundant_casts = True +warn_return_any = True +warn_unused_configs = True +exclude = ['build/'] diff --git a/setup.py b/setup.py index f3e5ffd..e27e61b 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,8 @@ install_requires=[ 'django >= 3.2', 'python-resize-image', - 'python_magic' + 'python_magic', + 'phonenumbers', ], classifiers=[ 'Programming Language :: Python :: 3.6',