Skip to main content

A plugin to show lint errors for IW

Project description

flake8-iw

Linters for some common issues we encounter.- flake8-iw

Building and testing

Run command to build wheel and tarball.

python3 -m build
twine check --strict dist/*
twine upload dist/*

Run command to test.

pytest

To run package in edit mode (to test on live code) from the directory where running flake8. This requires pip >= 21.3.1.

pip install -e ../flake8_iw/

Supported lint checks

IW01: Use of patch

Lint check to prevent the use of patch directly. Recommendation: Use PatchingTestCase / PatchingTransactionTestCase instead

Correct

from instawork.tests import PatchingTestCase


class SignUpUpdatedTests(PatchingTestCase):
    def setUp(self):
        self.mock_call = self.patch("apps.auth.signals.task_send_email.delay")

    def test_email(self):
        expect(self.mock_call).to(have_been_called_once)

    def test_sms(self):
        mock_sms = self.patch("apps.auth.signals.task_send_sms.delay")
        expect(mock_sms).to(have_been_called_once)

Wrong ⚠️

from unittest.mock import patch


class SignUpUpdatedTests(TestCase):
    def setUp(self):
        self.patcher = patch("apps.auth.signals.task_send_email.delay")
        self.mock_email = self.patcher.start()

    def tearDown(self):
        self.patcher.stop()

    def test_email(self):
        ...
        expect(self.mock_email).to(have_been_called_once)

    @patch("apps.auth.signals.task_send_sms.delay")
    def test_sms(self, mock_sms):
        ...
        expect(mock_sms).to(have_been_called_once)

IW02: Use of patch for time freeze

Lint check to prevent the use of patch to freeze time. Recommendation: Use freeze_time from PatchingTestCase / PatchingTransactionTestCase or use freeze_time decorator or context manager from freezegun package.

Correct

from django.utils import timezone
from instawork.tests import PatchingTestCase


class UserFeatureViewTests(PatchingTestCase):
    def setUp(self):
        self.now = timezone.now()

    def test_feature_view(self):
        ufv = None

        # Option 1
        with freeze_time(self.now):
            ufv = UserFeatureView.objects.get_or_create(
                user=self.shift.worker, feature=UserFeatureView.FEATURE_1
            )

        # Option 2
        self.freeze_time(self.now)
        ufv = UserFeatureView.objects.get_or_create(
            user=self.shift.worker, feature=UserFeatureView.FEATURE_1
        )

        ...

        expect(ufv.date_created).to(equal(self.now))

Wrong ⚠️

from django.utils import timezone
from instawork.tests import PatchingTestCase


class UserFeatureViewTests(PatchingTestCase):
    def setUp(self):
        self.now = timezone.now()
        self.mock_call = self.patch("django.utils.timezone.now", return_value=self.now)

    def test_feature_view(self):
        ufv = UserFeatureView.objects.get_or_create(
            user=self.shift.worker, feature=UserFeatureView.FEATURE_1
        )

        ...

        expect(ufv.date_created).to(equal(self.now))

IW03: Error logging without exception info (exc_info)

Lint check to prevent error logging without exception info. Recommendation: Add exc_info=True keyword argument in logger.error()

Correct

import logging

custom_logger = logging.getLogger("module.logger")

class UserFeatureView(Model):
    def save(self):
        try:
            ...
        except ValueError as e:
            custom_logger.error(e, exc_info=True)
            return name

Wrong ⚠️

import logging

custom_logger = logging.getLogger("module.logger")

class UserFeatureView(Model):
    def save(self):
        try:
            ...
        except ValueError as e:
            custom_logger.error(e)
            return name

IW04: Use of datetime.now

Lint to avoid usage of datetime.now() which does not contain timezone information and causes various warnings in tests. Use timezone.now() instead.

Correct

from django.utils import timezone

now = timezone.now()

Wrong ⚠️

from datetime import datetime

now = datetime.now()

IW05: Use of datetime.replace(tzinfo=XXX)

Lint to avoid usage of datetime.replace(tzinfo=XXX) which is not a viable way of setting timezones with python/pytz.

Correct

import pytz
from django.utils import timezone

tz = pytz.timezone("America/Los_Angeles")
now_pt = timezone.now().astimezone(tz)

Wrong ⚠️

import pytz
from django.utils import timezone

tz = pytz.timezone("America/Los_Angeles")
now_pt = timezone.now().replace(tzinfo=tz)

IW06: Use of bulk_update/bulk_create without batch_size

Lint to avoid usage of Model.objects.bulk_update / Model.objects.bulk_create. Use Model.objects.bulk_update(batch_size=X) / Model.objects.bulk_create(batch_size=X) instead.

Correct

# Bulk update
Model.objects.bulk_update([obj1, obj2, ...], batch_size=10)

# Bulk create
Model.objects.bulk_create([obj1, obj2, ...], batch_size=10)

Wrong ⚠️

# Bulk update
Model.objects.bulk_update([obj1, obj2, ...])

# Bulk create
Model.objects.bulk_create([obj1, obj2, ...])

IW07: Use of celery.shared_task, use instawork.decorators.shared_task

Use our internal decorator instead: instawork.decorators.shared_task.

Correct

from instawork.decorators import shared_task

@shared_task
def my_task():
    pass

Wrong ⚠️

from celery import shared_task

@shared_task
def my_task():
    pass

IW08: use of timezone.activate or timezone.deactivate, use with timezone.override instead

Lint to avoid usage of timezone.activate() and instead use with timezone.override(). This is to avoid timezone leakage between different tests and features.

Correct

from django.utils import timezone
with timezone.override(zoneinfo.ZoneInfo(tzname)):
    <Rest of the code>

Wrong ⚠️

from django.utils import timezone
timezone.activate(zoneinfo.ZoneInfo(tzname))
<Rest of the code>
timezone.deactivate()

IW09: missing db_constraint=False on foreign key

It's required to pass db_constraint=False when creating a new foreign key relationship. This is to prevent issues with online schema changes that arise due to MySQL's foreign key architecture.

Correct

x = models.ForeignKey(db_constraint=False, on_delete=models.CASCADE)

Wrong ⚠️

x = models.ForeignKey(on_delete=models.CASCADE)

IW10: on_delete=DO_NOTHING on foreign key

It's not advisable to use DO_NOTHING on foreign keys because we have removed foreign key constraints in the database. It's best to have a strategy that deals with deletions that doesn't leave "orphaned" foreign key ids.

Correct

x = models.ForeignKey(db_constraint=False, on_delete=models.CASCADE)

Wrong ⚠️

x = models.ForeignKey(db_constraint=False, on_delete=models.DO_NOTHING)

IW11: Use of db_index=True

Use Options.indexes to define an index rather than argument on field. See here

Correct

x = models.CharField()

class Meta:
    indexes = [
        models.Index(fields=["x"], name="x_idx"),
    ]

Wrong ⚠️

x = models.CharField(db_index=True)

IW12: Use of unique=True

Use Options.constraints to define uniqueness rather than argument on field. See here and here

Correct

x = models.CharField()

class Meta:
    constraints = [
        models.UniqueConstraint(fields=["x"], name="unique_x"),
    ]

Wrong ⚠️

x = models.CharField(unique=True)

IW13: Use of index_together instead of Options.indexes

Use Options.indexes to define an index rather than argument on field. See here

Correct

class Meta:
    indexes = [
        models.Index(fields=["a", "b"], name="a_b_idx"),
    ]

Wrong ⚠️

class Meta:
    index_together = (("a", "b"))

IW14: Use of unique_together instead of Options.constraints

Use Options.constraints to define uniqueness rather than argument on field. See here and here

Correct

class Meta:
    constraints = [
        models.UniqueConstraint(fields=["a", "b"], name="unique_a_b"),
    ]

Wrong ⚠️

class Meta:
    unique_together = (("a", "b"))

IW15: Use of timezone.now in Factories without LazyAttribute or LazyFunction

Use LazyAttribute or LazyFunction in factories with timezone.now to enable freezegun and avoid unexpected datetimes.

Correct

class TestFactory(factory.django.DjangoModelFactory):
    starts_at = factory.LazyFunction(timezone.now)

Wrong ⚠️

class TestFactory(factory.django.DjangoModelFactory):
    starts_at = timezone.now()

IW16: Using setUpClass and tearDownClass inside a test case, use setUpTestData instead

Avoid using setUpClass and tearDownClass inside a test case

Correct

class MyTest(TestCase):
    def setUpTestData(self):
        something()
    

Wrong ⚠️

class MyTest(TestCase):
    @classmethod
    def setUpClass(self):
        something()
    
    @classmethod
    def tearDownClass(self):
        something()

IW17: Parameterized must be the topmost decorator

parameterized.expand() must be above override_config, override_settings, etc.

Correct

class MyTest(TestCase):
    @parameterized.expand([(1,2)])
    @override_config(SOME_CONFIG=1)
    def test_something(self, p1, p2):
        something()

Wrong ⚠️

class MyTest(TestCase):
    @override_config(SOME_CONFIG=1)
    @parameterized.expand([(1,2)])
    def test_something(self, p1, p2):
        something()

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flake8_iw-0.0.24.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

flake8_iw-0.0.24-py3-none-any.whl (15.6 kB view details)

Uploaded Python 3

File details

Details for the file flake8_iw-0.0.24.tar.gz.

File metadata

  • Download URL: flake8_iw-0.0.24.tar.gz
  • Upload date:
  • Size: 13.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.17

File hashes

Hashes for flake8_iw-0.0.24.tar.gz
Algorithm Hash digest
SHA256 ab0edff8126bccac105d49ff3e6e19ca0ba0f9f3a4cf9c9c098a746ea68b5673
MD5 d4d6ee75ff3b51646ba761fa3d7f9ee6
BLAKE2b-256 a1c62a0cdc59826a8aad48c9f480ab8102bb823118491065a3e86636ac62b6d7

See more details on using hashes here.

File details

Details for the file flake8_iw-0.0.24-py3-none-any.whl.

File metadata

  • Download URL: flake8_iw-0.0.24-py3-none-any.whl
  • Upload date:
  • Size: 15.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.9.17

File hashes

Hashes for flake8_iw-0.0.24-py3-none-any.whl
Algorithm Hash digest
SHA256 1e0f12d5565a08cc9078d40546fd50bae36304194cbb6856823e008a6a67fb02
MD5 ab5e4e1294d37a2db5f68f6f0faf9c63
BLAKE2b-256 79a4373ca77c290e791af69998d739ef97f05949ca2774209438ec4666736a07

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page