From 0db8a991e35a5ea5459376bb206daa8fb8fa668d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Wed, 24 Aug 2022 19:22:19 +0300 Subject: [PATCH 1/6] Updated GH actions (#459) --- .github/workflows/python-package.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 6e8b5880..11f38167 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -19,12 +19,12 @@ jobs: python-version: [ 3.5, 3.6, 3.7, 3.8, 3.9, '3.10' ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - - uses: actions/cache@v2 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -43,9 +43,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: actions/cache@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -67,9 +67,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: actions/cache@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + - uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }} @@ -96,9 +96,9 @@ jobs: needs: [test, lint, i18n] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: casperdcl/deploy-pypi@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + - uses: casperdcl/deploy-pypi@v3 with: password: ${{ secrets.PYPI_API_TOKEN }} build: true From 5ac63f6117a73df7cf225f37dbbe8c033a916b21 Mon Sep 17 00:00:00 2001 From: cole-jacobs <39637602+cole-jacobs@users.noreply.github.com> Date: Wed, 24 Aug 2022 11:35:38 -0600 Subject: [PATCH 2/6] Pluggable Switch and Sample models #456 (#457) Remove direct references to `Switch` and `Sample` in favor of `get_waffle_switch_model` and `get_waffle_sample_model`. --- waffle/management/commands/waffle_delete.py | 13 ++-- waffle/management/commands/waffle_sample.py | 11 ++- waffle/management/commands/waffle_switch.py | 14 ++-- waffle/models.py | 21 +++--- waffle/tests/test_decorators.py | 17 +++-- waffle/tests/test_management.py | 43 ++++++------ waffle/tests/test_models.py | 25 ++++--- waffle/tests/test_testutils.py | 53 +++++++-------- waffle/tests/test_views.py | 13 ++-- waffle/tests/test_waffle.py | 75 ++++++++++++++------- waffle/testutils.py | 10 ++- 11 files changed, 172 insertions(+), 123 deletions(-) diff --git a/waffle/management/commands/waffle_delete.py b/waffle/management/commands/waffle_delete.py index 38c8066f..a1f161da 100644 --- a/waffle/management/commands/waffle_delete.py +++ b/waffle/management/commands/waffle_delete.py @@ -1,7 +1,10 @@ from django.core.management.base import BaseCommand -from waffle import get_waffle_flag_model -from waffle.models import Sample, Switch +from waffle import ( + get_waffle_flag_model, + get_waffle_switch_model, + get_waffle_sample_model, +) class Command(BaseCommand): @@ -37,14 +40,16 @@ def handle(self, *args, **options): switches = options['switch_names'] if switches: - switches_queryset = Switch.objects.filter(name__in=switches) + switches_queryset = get_waffle_switch_model().objects.filter( + name__in=switches + ) switch_count = switches_queryset.count() switches_queryset.delete() self.stdout.write('Deleted %s Switches' % switch_count) samples = options['sample_names'] if samples: - sample_queryset = Sample.objects.filter(name__in=samples) + sample_queryset = get_waffle_sample_model().objects.filter(name__in=samples) sample_count = sample_queryset.count() sample_queryset.delete() self.stdout.write('Deleted %s Samples' % sample_count) diff --git a/waffle/management/commands/waffle_sample.py b/waffle/management/commands/waffle_sample.py index 94437484..e239d8d5 100644 --- a/waffle/management/commands/waffle_sample.py +++ b/waffle/management/commands/waffle_sample.py @@ -1,7 +1,6 @@ from django.core.management.base import BaseCommand, CommandError -from waffle.models import Sample - +from waffle import get_waffle_sample_model class Command(BaseCommand): def add_arguments(self, parser): @@ -30,7 +29,7 @@ def add_arguments(self, parser): def handle(self, *args, **options): if options['list_samples']: self.stdout.write('Samples:') - for sample in Sample.objects.iterator(): + for sample in get_waffle_sample_model().objects.iterator(): self.stdout.write('%s: %.1f%%' % (sample.name, sample.percent)) self.stdout.write('') return @@ -51,14 +50,14 @@ def handle(self, *args, **options): raise CommandError('You need to enter a valid percentage value.') if options['create']: - sample, created = Sample.objects.get_or_create( + sample, created = get_waffle_sample_model().objects.get_or_create( name=sample_name, defaults={'percent': 0}) if created: self.stdout.write('Creating sample: %s' % sample_name) else: try: - sample = Sample.objects.get(name=sample_name) - except Sample.DoesNotExist: + sample = get_waffle_sample_model().objects.get(name=sample_name) + except get_waffle_sample_model().DoesNotExist: raise CommandError('This sample does not exist.') sample.percent = percent diff --git a/waffle/management/commands/waffle_switch.py b/waffle/management/commands/waffle_switch.py index c1e567b4..1e335bd1 100644 --- a/waffle/management/commands/waffle_switch.py +++ b/waffle/management/commands/waffle_switch.py @@ -1,7 +1,7 @@ from argparse import ArgumentTypeError from django.core.management.base import BaseCommand, CommandError -from waffle.models import Switch +from waffle import get_waffle_switch_model def on_off_bool(string): @@ -40,7 +40,7 @@ def add_arguments(self, parser): def handle(self, *args, **options): if options['list_switches']: self.stdout.write('Switches:') - for switch in Switch.objects.iterator(): + for switch in get_waffle_switch_model().objects.iterator(): self.stdout.write( '%s: %s' % (switch.name, 'on' if switch.active else 'off') ) @@ -53,14 +53,16 @@ def handle(self, *args, **options): if not (switch_name and state is not None): raise CommandError('You need to specify a switch name and state.') - if options['create']: - switch, created = Switch.objects.get_or_create(name=switch_name) + if options["create"]: + switch, created = get_waffle_switch_model().objects.get_or_create( + name=switch_name + ) if created: self.stdout.write('Creating switch: %s' % switch_name) else: try: - switch = Switch.objects.get(name=switch_name) - except Switch.DoesNotExist: + switch = get_waffle_switch_model().objects.get(name=switch_name) + except get_waffle_switch_model().DoesNotExist: raise CommandError('This switch does not exist.') switch.active = state diff --git a/waffle/models.py b/waffle/models.py index 9d1cea9e..56ef8a3f 100644 --- a/waffle/models.py +++ b/waffle/models.py @@ -8,7 +8,12 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from waffle import managers, get_waffle_flag_model +from waffle import ( + get_waffle_sample_model, + get_waffle_switch_model, + get_waffle_flag_model, + managers, +) from waffle.utils import get_setting, keyfmt, get_cache logger = logging.getLogger('waffle') @@ -460,11 +465,8 @@ def is_active(self): if log_level: logger.log(log_level, 'Switch %s not found', self.name) if get_setting('CREATE_MISSING_SWITCHES'): - switch, _created = Switch.objects.get_or_create( - name=self.name, - defaults={ - 'active': get_setting('SWITCH_DEFAULT') - } + switch, _created = get_waffle_switch_model().objects.get_or_create( + name=self.name, defaults={"active": get_setting("SWITCH_DEFAULT")} ) cache = get_cache() cache.set(self._cache_key(self.name), switch) @@ -531,11 +533,8 @@ def is_active(self): default_percent = 100 if get_setting('SAMPLE_DEFAULT') else 0 - sample, _created = Sample.objects.get_or_create( - name=self.name, - defaults={ - 'percent': default_percent - } + sample, _created = get_waffle_sample_model().objects.get_or_create( + name=self.name, defaults={"percent": default_percent} ) cache = get_cache() cache.set(self._cache_key(self.name), sample) diff --git a/waffle/tests/test_decorators.py b/waffle/tests/test_decorators.py index 5ea28da4..a31213f4 100644 --- a/waffle/tests/test_decorators.py +++ b/waffle/tests/test_decorators.py @@ -1,5 +1,4 @@ -from waffle import get_waffle_flag_model -from waffle.models import Switch +from waffle import get_waffle_flag_model, get_waffle_switch_model from waffle.tests.base import TestCase @@ -21,49 +20,49 @@ def test_flag_must_be_inactive(self): def test_switch_must_be_active(self): resp = self.client.get('/switch-on') self.assertEqual(404, resp.status_code) - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switch-on') self.assertEqual(200, resp.status_code) def test_switch_must_be_inactive(self): resp = self.client.get('/switch-off') self.assertEqual(200, resp.status_code) - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switch-off') self.assertEqual(404, resp.status_code) def test_switch_must_be_inactive_and_redirect_to_view(self): resp = self.client.get('/switched_view_with_valid_redirect') self.assertEqual(302, resp.status_code) - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switched_view_with_valid_redirect') self.assertEqual(200, resp.status_code) def test_switch_must_be_inactive_and_redirect_to_named_view(self): resp = self.client.get('/switched_view_with_valid_url_name') self.assertEqual(302, resp.status_code) - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switched_view_with_valid_url_name') self.assertEqual(200, resp.status_code) def test_switch_must_be_inactive_and_redirect_to_view_with_args(self): resp = self.client.get('/switched_view_with_args_with_valid_redirect/1/') self.assertRedirects(resp, '/foo_view_with_args/1/') - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switched_view_with_args_with_valid_redirect/1/') self.assertEqual(200, resp.status_code) def test_switch_must_be_inactive_and_redirect_to_named_view_with_args(self): resp = self.client.get('/switched_view_with_args_with_valid_url_name/1/') self.assertRedirects(resp, '/foo_view_with_args/1/') - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switched_view_with_args_with_valid_url_name/1/') self.assertEqual(200, resp.status_code) def test_switch_must_be_inactive_and_not_redirect(self): resp = self.client.get('/switched_view_with_invalid_redirect') self.assertEqual(404, resp.status_code) - Switch.objects.create(name='foo', active=True) + get_waffle_switch_model().objects.create(name='foo', active=True) resp = self.client.get('/switched_view_with_invalid_redirect') self.assertEqual(200, resp.status_code) diff --git a/waffle/tests/test_management.py b/waffle/tests/test_management.py index 59a78c48..2597b3d8 100644 --- a/waffle/tests/test_management.py +++ b/waffle/tests/test_management.py @@ -3,8 +3,11 @@ from django.core.management import call_command, CommandError from django.contrib.auth.models import Group, User -from waffle import get_waffle_flag_model -from waffle.models import Sample, Switch +from waffle import ( + get_waffle_flag_model, + get_waffle_switch_model, + get_waffle_sample_model, +) from waffle.tests.base import TestCase @@ -196,7 +199,7 @@ def test_create(self): percent = 20 call_command('waffle_sample', name, str(percent), create=True) - sample = Sample.objects.get(name=name) + sample = get_waffle_sample_model().objects.get(name=name) self.assertEqual(sample.percent, percent) def test_not_create(self): @@ -206,12 +209,12 @@ def test_not_create(self): name = 'test' with self.assertRaisesRegex(CommandError, 'This sample does not exist'): call_command('waffle_sample', name, '20') - self.assertFalse(Sample.objects.filter(name=name).exists()) + self.assertFalse(get_waffle_sample_model().objects.filter(name=name).exists()) def test_update(self): """ The command should update an existing sample. """ name = 'test' - sample = Sample.objects.create(name=name, percent=0) + sample = get_waffle_sample_model().objects.create(name=name, percent=0) self.assertEqual(sample.percent, 0) percent = 50 @@ -223,7 +226,7 @@ def test_update(self): def test_list(self): """ The command should list all samples.""" stdout = io.StringIO() - Sample.objects.create(name='test', percent=34) + get_waffle_sample_model().objects.create(name='test', percent=34) call_command('waffle_sample', list_samples=True, stdout=stdout) expected = 'Samples:\ntest: 34.0%' @@ -237,11 +240,11 @@ def test_create(self): name = 'test' call_command('waffle_switch', name, 'on', create=True) - switch = Switch.objects.get(name=name, active=True) + switch = get_waffle_switch_model().objects.get(name=name, active=True) switch.delete() call_command('waffle_switch', name, 'off', create=True) - Switch.objects.get(name=name, active=False) + get_waffle_switch_model().objects.get(name=name, active=False) def test_not_create(self): """ The command shouldn't create a new switch if the create flag is @@ -250,12 +253,12 @@ def test_not_create(self): name = 'test' with self.assertRaisesRegex(CommandError, 'This switch does not exist.'): call_command('waffle_switch', name, 'on') - self.assertFalse(Switch.objects.filter(name=name).exists()) + self.assertFalse(get_waffle_switch_model().objects.filter(name=name).exists()) def test_update(self): """ The command should update an existing switch. """ name = 'test' - switch = Switch.objects.create(name=name, active=True) + switch = get_waffle_switch_model().objects.create(name=name, active=True) call_command('waffle_switch', name, 'off') switch.refresh_from_db() @@ -268,8 +271,8 @@ def test_update(self): def test_list(self): """ The command should list all switches.""" stdout = io.StringIO() - Switch.objects.create(name='switch1', active=True) - Switch.objects.create(name='switch2', active=False) + get_waffle_switch_model().objects.create(name='switch1', active=True) + get_waffle_switch_model().objects.create(name='switch2', active=False) call_command('waffle_switch', list_switches=True, stdout=stdout) expected = 'Switches:\nswitch1: on\nswitch2: off' @@ -289,31 +292,31 @@ def test_delete_flag(self): def test_delete_swtich(self): """ The command should delete a switch. """ name = 'test_switch' - Switch.objects.create(name=name) + get_waffle_switch_model().objects.create(name=name) call_command('waffle_delete', switch_names=[name]) - self.assertEqual(Switch.objects.count(), 0) + self.assertEqual(get_waffle_switch_model().objects.count(), 0) def test_delete_sample(self): """ The command should delete a sample. """ name = 'test_sample' - Sample.objects.create(name=name, percent=0) + get_waffle_sample_model().objects.create(name=name, percent=0) call_command('waffle_delete', sample_names=[name]) - self.assertEqual(Sample.objects.count(), 0) + self.assertEqual(get_waffle_sample_model().objects.count(), 0) def test_delete_mix_of_types(self): """ The command should delete different types of records. """ name = 'test' get_waffle_flag_model().objects.create(name=name) - Switch.objects.create(name=name) - Sample.objects.create(name=name, percent=0) + get_waffle_switch_model().objects.create(name=name) + get_waffle_sample_model().objects.create(name=name, percent=0) call_command('waffle_delete', switch_names=[name], flag_names=[name], sample_names=[name]) self.assertEqual(get_waffle_flag_model().objects.count(), 0) - self.assertEqual(Switch.objects.count(), 0) - self.assertEqual(Sample.objects.count(), 0) + self.assertEqual(get_waffle_switch_model().objects.count(), 0) + self.assertEqual(get_waffle_sample_model().objects.count(), 0) def test_delete_some_but_not_all_records(self): """ The command should delete specified records, but leave records diff --git a/waffle/tests/test_models.py b/waffle/tests/test_models.py index ccfcf633..4d64d5dc 100644 --- a/waffle/tests/test_models.py +++ b/waffle/tests/test_models.py @@ -1,23 +1,32 @@ from django.test import TestCase -from waffle import get_waffle_flag_model -from waffle.models import Switch, Sample +from waffle import ( + get_waffle_flag_model, + get_waffle_sample_model, + get_waffle_switch_model, +) class ModelsTests(TestCase): def test_natural_keys(self): flag = get_waffle_flag_model().objects.create(name='test-flag') - switch = Switch.objects.create(name='test-switch') - sample = Sample.objects.create(name='test-sample', percent=0) + switch = get_waffle_switch_model().objects.create(name='test-switch') + sample = get_waffle_sample_model().objects.create(name='test-sample', percent=0) self.assertEqual(flag.natural_key(), ('test-flag',)) self.assertEqual(switch.natural_key(), ('test-switch',)) self.assertEqual(sample.natural_key(), ('test-sample',)) - self.assertEqual(get_waffle_flag_model().objects.get_by_natural_key('test-flag'), flag) - self.assertEqual(Switch.objects.get_by_natural_key('test-switch'), switch) - self.assertEqual(Sample.objects.get_by_natural_key('test-sample'), sample) + self.assertEqual( + get_waffle_flag_model().objects.get_by_natural_key("test-flag"), flag + ) + self.assertEqual( + get_waffle_switch_model().objects.get_by_natural_key("test-switch"), switch + ) + self.assertEqual( + get_waffle_sample_model().objects.get_by_natural_key("test-sample"), sample + ) def test_flag_is_not_active_for_none_requests(self): flag = get_waffle_flag_model().objects.create(name='test-flag') - self.assertEqual(flag.is_active(None), False) \ No newline at end of file + self.assertEqual(flag.is_active(None), False) diff --git a/waffle/tests/test_testutils.py b/waffle/tests/test_testutils.py index 4eea266c..fd2d92ae 100644 --- a/waffle/tests/test_testutils.py +++ b/waffle/tests/test_testutils.py @@ -5,13 +5,12 @@ from django.test import TransactionTestCase, RequestFactory, TestCase import waffle -from waffle.models import Switch, Sample from waffle.testutils import override_switch, override_flag, override_sample class OverrideSwitchMixin: def test_switch_existed_and_was_active(self): - Switch.objects.create(name='foo', active=True) + waffle.get_waffle_switch_model().objects.create(name='foo', active=True) with override_switch('foo', active=True): assert waffle.switch_is_active('foo') @@ -20,10 +19,10 @@ def test_switch_existed_and_was_active(self): assert not waffle.switch_is_active('foo') # make sure it didn't change 'active' value - assert Switch.objects.get(name='foo').active + assert waffle.get_waffle_switch_model().objects.get(name='foo').active def test_switch_existed_and_was_NOT_active(self): - Switch.objects.create(name='foo', active=False) + waffle.get_waffle_switch_model().objects.create(name='foo', active=False) with override_switch('foo', active=True): assert waffle.switch_is_active('foo') @@ -32,10 +31,10 @@ def test_switch_existed_and_was_NOT_active(self): assert not waffle.switch_is_active('foo') # make sure it didn't change 'active' value - assert not Switch.objects.get(name='foo').active + assert not waffle.get_waffle_switch_model().objects.get(name='foo').active def test_new_switch(self): - assert not Switch.objects.filter(name='foo').exists() + assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() with override_switch('foo', active=True): assert waffle.switch_is_active('foo') @@ -43,10 +42,10 @@ def test_new_switch(self): with override_switch('foo', active=False): assert not waffle.switch_is_active('foo') - assert not Switch.objects.filter(name='foo').exists() + assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() def test_as_decorator(self): - assert not Switch.objects.filter(name='foo').exists() + assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() @override_switch('foo', active=True) def test_enabled(): @@ -60,10 +59,10 @@ def test_disabled(): test_disabled() - assert not Switch.objects.filter(name='foo').exists() + assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() def test_restores_after_exception(self): - Switch.objects.create(name='foo', active=True) + waffle.get_waffle_switch_model().objects.create(name='foo', active=True) def inner(): with override_switch('foo', active=False): @@ -72,10 +71,10 @@ def inner(): with self.assertRaises(RuntimeError): inner() - assert Switch.objects.get(name='foo').active + assert waffle.get_waffle_switch_model().objects.get(name='foo').active def test_restores_after_exception_in_decorator(self): - Switch.objects.create(name='foo', active=True) + waffle.get_waffle_switch_model().objects.create(name='foo', active=True) @override_switch('foo', active=False) def inner(): @@ -84,10 +83,10 @@ def inner(): with self.assertRaises(RuntimeError): inner() - assert Switch.objects.get(name='foo').active + assert waffle.get_waffle_switch_model().objects.get(name='foo').active def test_cache_is_flushed_by_testutils_even_in_transaction(self): - Switch.objects.create(name='foo', active=True) + waffle.get_waffle_switch_model().objects.create(name='foo', active=True) with transaction.atomic(): with override_switch('foo', active=True): @@ -189,7 +188,7 @@ class OverrideFlagsTransactionTestCase(OverrideFlagTestsMixin, TransactionTestCa class OverrideSampleTestsMixin: def test_sample_existed_and_was_100(self): - Sample.objects.create(name='foo', percent='100.0') + waffle.get_waffle_sample_model().objects.create(name='foo', percent='100.0') with override_sample('foo', active=True): assert waffle.sample_is_active('foo') @@ -198,10 +197,10 @@ def test_sample_existed_and_was_100(self): assert not waffle.sample_is_active('foo') self.assertEqual(Decimal('100.0'), - Sample.objects.get(name='foo').percent) + waffle.get_waffle_sample_model().objects.get(name='foo').percent) def test_sample_existed_and_was_0(self): - Sample.objects.create(name='foo', percent='0.0') + waffle.get_waffle_sample_model().objects.create(name='foo', percent='0.0') with override_sample('foo', active=True): assert waffle.sample_is_active('foo') @@ -210,10 +209,10 @@ def test_sample_existed_and_was_0(self): assert not waffle.sample_is_active('foo') self.assertEqual(Decimal('0.0'), - Sample.objects.get(name='foo').percent) + waffle.get_waffle_sample_model().objects.get(name='foo').percent) def test_sample_existed_and_was_50(self): - Sample.objects.create(name='foo', percent='50.0') + waffle.get_waffle_sample_model().objects.create(name='foo', percent='50.0') with override_sample('foo', active=True): assert waffle.sample_is_active('foo') @@ -222,10 +221,10 @@ def test_sample_existed_and_was_50(self): assert not waffle.sample_is_active('foo') self.assertEqual(Decimal('50.0'), - Sample.objects.get(name='foo').percent) + waffle.get_waffle_sample_model().objects.get(name='foo').percent) def test_sample_did_not_exist(self): - assert not Sample.objects.filter(name='foo').exists() + assert not waffle.get_waffle_sample_model().objects.filter(name='foo').exists() with override_sample('foo', active=True): assert waffle.sample_is_active('foo') @@ -233,10 +232,10 @@ def test_sample_did_not_exist(self): with override_sample('foo', active=False): assert not waffle.sample_is_active('foo') - assert not Sample.objects.filter(name='foo').exists() + assert not waffle.get_waffle_sample_model().objects.filter(name='foo').exists() def test_cache_is_flushed_by_testutils_even_in_transaction(self): - Sample.objects.create(name='foo', percent='100.0') + waffle.get_waffle_sample_model().objects.create(name='foo', percent='100.0') with transaction.atomic(): with override_sample('foo', active=True): @@ -264,8 +263,8 @@ class OverrideSwitchOnClassTestsMixin(object): @classmethod def setUpClass(cls): super(OverrideSwitchOnClassTestsMixin, cls).setUpClass() - assert not Switch.objects.filter(name='foo').exists() - Switch.objects.create(name='foo', active=True) + assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() + waffle.get_waffle_switch_model().objects.create(name='foo', active=True) def test_undecorated_method_is_set_properly_for_switch(self): self.assertFalse(waffle.switch_is_active('foo')) @@ -318,8 +317,8 @@ class OverrideSampleOnClassTestsMixin(object): @classmethod def setUpClass(cls): super(OverrideSampleOnClassTestsMixin, cls).setUpClass() - assert not Sample.objects.filter(name='foo').exists() - Sample.objects.create(name='foo', percent='100.0') + assert not waffle.get_waffle_sample_model().objects.filter(name='foo').exists() + waffle.get_waffle_sample_model().objects.create(name='foo', percent='100.0') def test_undecorated_method_is_set_properly_for_sample(self): self.assertFalse(waffle.sample_is_active('foo')) diff --git a/waffle/tests/test_views.py b/waffle/tests/test_views.py index f6d4e04c..8259dcc7 100644 --- a/waffle/tests/test_views.py +++ b/waffle/tests/test_views.py @@ -1,7 +1,10 @@ from django.urls import reverse -from waffle import get_waffle_flag_model, get_waffle_sample_model, get_waffle_switch_model -from waffle.models import Sample, Switch +from waffle import ( + get_waffle_flag_model, + get_waffle_sample_model, + get_waffle_switch_model, +) from waffle.tests.base import TestCase @@ -63,7 +66,7 @@ def test_flush_all_flags(self): def test_flush_all_switches(self): """Test the 'SWITCHES_ALL' list gets invalidated correctly.""" - switch = Switch.objects.create(name='myswitch', active=True) + switch = get_waffle_switch_model().objects.create(name='myswitch', active=True) response = self.client.get(reverse('wafflejs')) self.assertEqual(200, response.status_code) assert ('myswitch', True) in response.context['switches'] @@ -76,12 +79,12 @@ def test_flush_all_switches(self): def test_flush_all_samples(self): """Test the 'SAMPLES_ALL' list gets invalidated correctly.""" - Sample.objects.create(name='sample1', percent='100.0') + get_waffle_sample_model().objects.create(name='sample1', percent='100.0') response = self.client.get(reverse('wafflejs')) self.assertEqual(200, response.status_code) assert ('sample1', True) in response.context['samples'] - Sample.objects.create(name='sample2', percent='100.0') + get_waffle_sample_model().objects.create(name='sample2', percent='100.0') response = self.client.get(reverse('wafflejs')) self.assertEqual(200, response.status_code) diff --git a/waffle/tests/test_waffle.py b/waffle/tests/test_waffle.py index 275037e1..77f04739 100644 --- a/waffle/tests/test_waffle.py +++ b/waffle/tests/test_waffle.py @@ -17,7 +17,6 @@ from test_app import views from test_app.models import CompanyAwareFlag, CustomSample, CustomSwitch, Company from waffle.middleware import WaffleMiddleware -from waffle.models import Sample, Switch from waffle.tests.base import TestCase @@ -276,7 +275,9 @@ def test_everyone_on(self): def test_everyone_off(self): """Test the 'everyone' switch off.""" - waffle.get_waffle_flag_model().objects.create(name='myflag', everyone=False, authenticated=True) + waffle.get_waffle_flag_model().objects.create( + name="myflag", everyone=False, authenticated=True + ) request = get() request.COOKIES['dwf_myflag'] = 'True' @@ -407,7 +408,10 @@ def test_everyone_on_read_from_write_db(self): response = process_request(request, views.flag_in_view) self.assertEqual(b'on', response.content) - @override_settings(WAFFLE_FLAG_MODEL='test_app.CompanyAwareFlag', AUTH_USER_MODEL='test_app.CompanyUser') + @override_settings( + WAFFLE_FLAG_MODEL="test_app.CompanyAwareFlag", + AUTH_USER_MODEL="test_app.CompanyUser", + ) def test_pluggable_flag_model(self): flag_model = waffle.get_waffle_flag_model() self.assertEqual(CompanyAwareFlag, flag_model) @@ -415,7 +419,9 @@ def test_pluggable_flag_model(self): acme_company = Company.objects.create(name='Acme Ltd.') feline_company = Company.objects.create(name='Feline LLC') - acme_company_flag = waffle.get_waffle_flag_model().objects.create(name='myflag', superusers=True) + acme_company_flag = waffle.get_waffle_flag_model().objects.create( + name="myflag", superusers=True + ) acme_company_flag.companies.add(acme_company) request = get() @@ -465,11 +471,11 @@ class SwitchTests(TestCase): def assert_switch_dynamically_created_with_value(self, expected_value): SWITCH_NAME = 'my_dynamically_created_switch' - assert Switch.objects.count() == 0 + assert waffle.get_waffle_switch_model().objects.count() == 0 assert expected_value == waffle.switch_is_active(SWITCH_NAME) - assert Switch.objects.count() == 1 + assert waffle.get_waffle_switch_model().objects.count() == 1 - switch = Switch.objects.get(name=SWITCH_NAME) + switch = waffle.get_waffle_switch_model().objects.get(name=SWITCH_NAME) assert switch.name == SWITCH_NAME assert expected_value == switch.active @@ -479,16 +485,22 @@ def assert_switch_dynamically_created_with_value(self, expected_value): assert expected_value == waffle.switch_is_active(SWITCH_NAME) def test_switch_active(self): - switch = Switch.objects.create(name='myswitch', active=True) + switch = waffle.get_waffle_switch_model().objects.create( + name="myswitch", active=True + ) assert waffle.switch_is_active(switch.name) def test_switch_inactive(self): - switch = Switch.objects.create(name='myswitch', active=False) + switch = waffle.get_waffle_switch_model().objects.create( + name="myswitch", active=False + ) assert not waffle.switch_is_active(switch.name) def test_switch_active_from_cache(self): """Do not make two queries for an existing active switch.""" - switch = Switch.objects.create(name='myswitch', active=True) + switch = waffle.get_waffle_switch_model().objects.create( + name="myswitch", active=True + ) # Get the value once so that it will be put into the cache assert waffle.switch_is_active(switch.name) queries = len(connection.queries) @@ -497,7 +509,9 @@ def test_switch_active_from_cache(self): def test_switch_inactive_from_cache(self): """Do not make two queries for an existing inactive switch.""" - switch = Switch.objects.create(name='myswitch', active=False) + switch = waffle.get_waffle_switch_model().objects.create( + name="myswitch", active=False + ) # Get the value once so that it will be put into the cache assert not waffle.switch_is_active(switch.name) queries = len(connection.queries) @@ -514,7 +528,7 @@ def test_undefined_default(self): @override_settings(DEBUG=True) def test_no_query(self): """Do not make two queries for a non-existent switch.""" - assert not Switch.objects.filter(name='foo').exists() + assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() queries = len(connection.queries) assert not waffle.switch_is_active('foo') assert len(connection.queries) > queries @@ -524,7 +538,9 @@ def test_no_query(self): @override_settings(DATABASE_ROUTERS=['waffle.tests.base.ReplicationRouter']) def test_read_from_write_db(self): - switch = Switch.objects.create(name='switch', active=True) + switch = waffle.get_waffle_switch_model().objects.create( + name="switch", active=True + ) # By default, switch_is_active should hit whatever it configured as the # read DB (so values will be stale if replication is lagged). @@ -567,8 +583,10 @@ def test_no_logging_missing_switch_by_default(self, mock_logger): @override_settings(WAFFLE_LOG_MISSING_SWITCHES=logging.WARNING) @mock.patch('waffle.models.logger') def test_logging_missing_switch(self, mock_logger): - waffle.switch_is_active('foo') - mock_logger.log.assert_called_with(logging.WARNING, 'Switch %s not found', 'foo') + waffle.switch_is_active("foo") + mock_logger.log.assert_called_with( + logging.WARNING, "Switch %s not found", "foo" + ) class SampleTests(TestCase): @@ -577,11 +595,11 @@ class SampleTests(TestCase): def assert_sample_dynamically_created_with_value(self, is_active, expected_value): SAMPLE_NAME = 'my_dynamically_created_sample' - assert Sample.objects.count() == 0 + assert waffle.get_waffle_sample_model().objects.count() == 0 assert is_active == waffle.sample_is_active(SAMPLE_NAME) - assert Sample.objects.count() == 1 + assert waffle.get_waffle_sample_model().objects.count() == 1 - sample = Sample.objects.get(name=SAMPLE_NAME) + sample = waffle.get_waffle_sample_model().objects.get(name=SAMPLE_NAME) assert sample.name == SAMPLE_NAME assert sample.percent == expected_value @@ -592,11 +610,15 @@ def assert_sample_dynamically_created_with_value(self, is_active, expected_value assert is_active == waffle.sample_is_active(SAMPLE_NAME) def test_sample_100(self): - sample = Sample.objects.create(name='sample', percent='100.0') + sample = waffle.get_waffle_sample_model().objects.create( + name="sample", percent="100.0" + ) assert waffle.sample_is_active(sample.name) def test_sample_0(self): - sample = Sample.objects.create(name='sample', percent='0.0') + sample = waffle.get_waffle_sample_model().objects.create( + name="sample", percent="0.0" + ) assert not waffle.sample_is_active(sample.name) def test_undefined(self): @@ -608,7 +630,9 @@ def test_undefined_default(self): @override_settings(DATABASE_ROUTERS=['waffle.tests.base.ReplicationRouter']) def test_read_from_write_db(self): - sample = Sample.objects.create(name='sample', percent='100.0') + sample = waffle.get_waffle_sample_model().objects.create( + name="sample", percent="100.0" + ) # By default, sample_is_active should hit whatever it configured as the # read DB (so values will be stale if replication is lagged). @@ -732,8 +756,9 @@ def toggle_is_active(self, flag): class SwitchTransactionTests(TransactionTestMixin, TransactionTestCase): def create_toggle(self): - return Switch.objects.create(name='transaction-switch-name', - active=False) + return waffle.get_waffle_switch_model().objects.create( + name="transaction-switch-name", active=False + ) def flip_toggle(self, switch): switch.active = True @@ -745,7 +770,9 @@ def toggle_is_active(self, switch): class SampleTransactionTests(TransactionTestMixin, TransactionTestCase): def create_toggle(self): - return Sample.objects.create(name='transaction-sample-name', percent=0) + return waffle.get_waffle_sample_model().objects.create( + name="transaction-sample-name", percent=0 + ) def flip_toggle(self, sample): sample.percent = 100 diff --git a/waffle/testutils.py b/waffle/testutils.py index c889b482..4b357df1 100644 --- a/waffle/testutils.py +++ b/waffle/testutils.py @@ -1,6 +1,10 @@ from django.test.utils import TestContextDecorator -from waffle import get_waffle_flag_model +from waffle import ( + get_waffle_flag_model, + get_waffle_sample_model, + get_waffle_switch_model, +) from waffle.models import Switch, Sample @@ -58,7 +62,7 @@ def test_happy_mode_enabled(): ... """ - cls = Switch + cls = get_waffle_switch_model() def update(self, active): obj = self.cls.objects.get(pk=self.obj.pk) @@ -84,7 +88,7 @@ def get_value(self): class override_sample(_overrider): - cls = Sample + cls = get_waffle_sample_model() def get(self): try: From fe40fedec53c686935f183648c8bdc43aba58b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Fri, 26 Aug 2022 17:06:28 +0300 Subject: [PATCH 3/6] Removed support for EOL Python versions (#460) --- .github/workflows/python-package.yml | 2 +- docs/conf.py | 2 -- setup.py | 2 -- test_app/models.py | 4 ++-- test_app/views.py | 10 +++++----- tox.ini | 5 +---- waffle/admin.py | 4 ++-- waffle/management/commands/waffle_flag.py | 2 +- waffle/management/commands/waffle_sample.py | 2 +- waffle/management/commands/waffle_switch.py | 2 +- waffle/managers.py | 2 +- waffle/migrations/0001_initial.py | 1 - waffle/migrations/0002_auto_20161201_0958.py | 1 - waffle/mixins.py | 8 ++++---- waffle/models.py | 10 +++++----- waffle/templatetags/waffle_tags.py | 6 ++---- waffle/tests/base.py | 4 ++-- waffle/tests/test_admin.py | 5 +---- waffle/tests/test_testutils.py | 12 ++++++------ waffle/tests/test_waffle.py | 4 ++-- waffle/testutils.py | 4 ++-- 21 files changed, 39 insertions(+), 53 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 11f38167..11846a0a 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ 3.5, 3.6, 3.7, 3.8, 3.9, '3.10' ] + python-version: [ 3.7, 3.8, 3.9, '3.10' ] steps: - uses: actions/checkout@v3 diff --git a/docs/conf.py b/docs/conf.py index 597b64a8..65953ffe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- -# # django-waffle documentation build configuration file, created by # sphinx-quickstart on Wed Aug 1 17:45:05 2012. # diff --git a/setup.py b/setup.py index 5ae93959..48423f2c 100644 --- a/setup.py +++ b/setup.py @@ -29,8 +29,6 @@ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', diff --git a/test_app/models.py b/test_app/models.py index b1f49e9a..5003d1a3 100644 --- a/test_app/models.py +++ b/test_app/models.py @@ -50,14 +50,14 @@ class CompanyAwareFlag(AbstractUserFlag): ) def get_flush_keys(self, flush_keys=None): - flush_keys = super(CompanyAwareFlag, self).get_flush_keys(flush_keys) + flush_keys = super().get_flush_keys(flush_keys) companies_cache_key = get_setting(CompanyAwareFlag.FLAG_COMPANIES_CACHE_KEY, CompanyAwareFlag.FLAG_COMPANIES_CACHE_KEY_DEFAULT) flush_keys.append(keyfmt(companies_cache_key, self.name)) return flush_keys def is_active_for_user(self, user): - is_active = super(CompanyAwareFlag, self).is_active_for_user(user) + is_active = super().is_active_for_user(user) if is_active: return is_active diff --git a/test_app/views.py b/test_app/views.py index 0bcc33f9..6ba910a9 100644 --- a/test_app/views.py +++ b/test_app/views.py @@ -62,7 +62,7 @@ def foo_view(request): def foo_view_with_args(request, some_number): - return HttpResponse('redirected with {}'.format(some_number)) + return HttpResponse(f'redirected with {some_number}') @waffle_switch('foo', redirect_to=foo_view) @@ -77,12 +77,12 @@ def switched_view_with_valid_url_name(request): @waffle_switch('foo', redirect_to=foo_view_with_args) def switched_view_with_args_with_valid_redirect(request, some_number): - return HttpResponse('foo with {}'.format(some_number)) + return HttpResponse(f'foo with {some_number}') @waffle_switch('foo', redirect_to='foo_view_with_args') def switched_view_with_args_with_valid_url_name(request, some_number): - return HttpResponse('foo with {}'.format(some_number)) + return HttpResponse(f'foo with {some_number}') @waffle_switch('foo', redirect_to='invalid_view') @@ -102,12 +102,12 @@ def flagged_view_with_valid_url_name(request): @waffle_flag('foo', redirect_to=foo_view_with_args) def flagged_view_with_args_with_valid_redirect(request, some_number): - return HttpResponse('foo with {}'.format(some_number)) + return HttpResponse(f'foo with {some_number}') @waffle_flag('foo', redirect_to='foo_view_with_args') def flagged_view_with_args_with_valid_url_name(request, some_number): - return HttpResponse('foo with {}'.format(some_number)) + return HttpResponse(f'foo with {some_number}') @waffle_flag('foo', redirect_to='invalid_view') diff --git a/tox.ini b/tox.ini index 73231f82..e774a712 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,10 @@ [tox] envlist = - py35-django22 - py{36,37,38,39}-django{22,30,31,32} + py{37,38,39}-django{22,30,31,32} py{310}-django{32,40} [gh-actions] python = - 3.5: py35 - 3.6: py36 3.7: py37 3.8: py38 3.9: py39 diff --git a/waffle/admin.py b/waffle/admin.py index 259bccec..130b208f 100755 --- a/waffle/admin.py +++ b/waffle/admin.py @@ -13,7 +13,7 @@ class BaseAdmin(admin.ModelAdmin): search_fields = ('name', 'note') def get_actions(self, request): - actions = super(BaseAdmin, self).get_actions(request) + actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions @@ -97,7 +97,7 @@ def formfield_for_dbfield(self, db_field, **kwargs): self.admin_site, using=kwargs.get("using")) return db_field.formfield(**kwargs) - return super(FlagAdmin, self).formfield_for_dbfield(db_field, **kwargs) + return super().formfield_for_dbfield(db_field, **kwargs) def enable_switches(ma, request, qs): diff --git a/waffle/management/commands/waffle_flag.py b/waffle/management/commands/waffle_flag.py index 6a674fc9..794e6133 100644 --- a/waffle/management/commands/waffle_flag.py +++ b/waffle/management/commands/waffle_flag.py @@ -167,7 +167,7 @@ def handle(self, *args, **options): # for user in user_hash: flag.users.add(*[user.id for user in user_hash]) elif hasattr(flag, option): - self.stdout.write('Setting %s: %s' % (option, options[option])) + self.stdout.write(f'Setting {option}: {options[option]}') setattr(flag, option, options[option]) flag.save() diff --git a/waffle/management/commands/waffle_sample.py b/waffle/management/commands/waffle_sample.py index e239d8d5..9613ad9c 100644 --- a/waffle/management/commands/waffle_sample.py +++ b/waffle/management/commands/waffle_sample.py @@ -30,7 +30,7 @@ def handle(self, *args, **options): if options['list_samples']: self.stdout.write('Samples:') for sample in get_waffle_sample_model().objects.iterator(): - self.stdout.write('%s: %.1f%%' % (sample.name, sample.percent)) + self.stdout.write(f'{sample.name}: {sample.percent:.1f}%') self.stdout.write('') return diff --git a/waffle/management/commands/waffle_switch.py b/waffle/management/commands/waffle_switch.py index 1e335bd1..68b40db3 100644 --- a/waffle/management/commands/waffle_switch.py +++ b/waffle/management/commands/waffle_switch.py @@ -42,7 +42,7 @@ def handle(self, *args, **options): self.stdout.write('Switches:') for switch in get_waffle_switch_model().objects.iterator(): self.stdout.write( - '%s: %s' % (switch.name, 'on' if switch.active else 'off') + '{}: {}'.format(switch.name, 'on' if switch.active else 'off') ) self.stdout.write('') return diff --git a/waffle/managers.py b/waffle/managers.py index 099b3435..64b9c661 100644 --- a/waffle/managers.py +++ b/waffle/managers.py @@ -11,7 +11,7 @@ def get_by_natural_key(self, name): def create(self, *args, **kwargs): cache = get_cache() - ret = super(BaseManager, self).create(*args, **kwargs) + ret = super().create(*args, **kwargs) cache_key = get_setting(self.KEY_SETTING) cache.delete(cache_key) return ret diff --git a/waffle/migrations/0001_initial.py b/waffle/migrations/0001_initial.py index 6bb90c8d..a5636e52 100644 --- a/waffle/migrations/0001_initial.py +++ b/waffle/migrations/0001_initial.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from django.db import models, migrations import django.utils.timezone from django.conf import settings diff --git a/waffle/migrations/0002_auto_20161201_0958.py b/waffle/migrations/0002_auto_20161201_0958.py index 051f4968..11f1ab23 100644 --- a/waffle/migrations/0002_auto_20161201_0958.py +++ b/waffle/migrations/0002_auto_20161201_0958.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.10.3 on 2016-12-01 09:58 from django.db import migrations, models diff --git a/waffle/mixins.py b/waffle/mixins.py index c259b14a..590aa524 100644 --- a/waffle/mixins.py +++ b/waffle/mixins.py @@ -5,7 +5,7 @@ from waffle import switch_is_active, flag_is_active, sample_is_active -class BaseWaffleMixin(object): +class BaseWaffleMixin: def validate_waffle(self, waffle, func): if waffle.startswith('!'): @@ -33,7 +33,7 @@ def dispatch(self, request, *args, **kwargs): if not active: return self.invalid_waffle() - return super(WaffleFlagMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) class WaffleSampleMixin(BaseWaffleMixin): @@ -50,7 +50,7 @@ def dispatch(self, request, *args, **kwargs): if not active: return self.invalid_waffle() - return super(WaffleSampleMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) class WaffleSwitchMixin(BaseWaffleMixin): @@ -67,4 +67,4 @@ def dispatch(self, request, *args, **kwargs): if not active: return self.invalid_waffle() - return super(WaffleSwitchMixin, self).dispatch(request, *args, **kwargs) + return super().dispatch(request, *args, **kwargs) diff --git a/waffle/models.py b/waffle/models.py index 56ef8a3f..7d2226cf 100644 --- a/waffle/models.py +++ b/waffle/models.py @@ -24,7 +24,7 @@ class BaseModel(models.Model): SINGLE_CACHE_KEY = '' ALL_CACHE_KEY = '' - class Meta(object): + class Meta: abstract = True def __str__(self): @@ -98,7 +98,7 @@ def flush(self): def save(self, *args, **kwargs): self.modified = timezone.now() - ret = super(BaseModel, self).save(*args, **kwargs) + ret = super().save(*args, **kwargs) if hasattr(transaction, 'on_commit'): transaction.on_commit(self.flush) else: @@ -106,7 +106,7 @@ def save(self, *args, **kwargs): return ret def delete(self, *args, **kwargs): - ret = super(BaseModel, self).delete(*args, **kwargs) + ret = super().delete(*args, **kwargs) if hasattr(transaction, 'on_commit'): transaction.on_commit(self.flush) else: @@ -342,7 +342,7 @@ class Meta(AbstractBaseFlag.Meta): verbose_name_plural = _('Flags') def get_flush_keys(self, flush_keys=None): - flush_keys = super(AbstractUserFlag, self).get_flush_keys(flush_keys) + flush_keys = super().get_flush_keys(flush_keys) flush_keys.extend([ keyfmt(get_setting('FLAG_USERS_CACHE_KEY'), self.name), keyfmt(get_setting('FLAG_GROUPS_CACHE_KEY'), self.name), @@ -384,7 +384,7 @@ def _get_group_ids(self): return group_ids def is_active_for_user(self, user): - is_active = super(AbstractUserFlag, self).is_active_for_user(user) + is_active = super().is_active_for_user(user) if is_active: return is_active diff --git a/waffle/templatetags/waffle_tags.py b/waffle/templatetags/waffle_tags.py index ef34be58..38783f1e 100644 --- a/waffle/templatetags/waffle_tags.py +++ b/waffle/templatetags/waffle_tags.py @@ -23,10 +23,8 @@ def __repr__(self): return '' % self.name def __iter__(self): - for node in self.nodelist_true: - yield node - for node in self.nodelist_false: - yield node + yield from self.nodelist_true + yield from self.nodelist_false def render(self, context): try: diff --git a/waffle/tests/base.py b/waffle/tests/base.py index 4ed73ecc..a5b4e673 100644 --- a/waffle/tests/base.py +++ b/waffle/tests/base.py @@ -6,10 +6,10 @@ class TestCase(test.TransactionTestCase): def _pre_setup(self): cache.cache.clear() - super(TestCase, self)._pre_setup() + super()._pre_setup() -class ReplicationRouter(object): +class ReplicationRouter: """Router for simulating an environment with DB replication This router directs all DB reads to a completely different database than diff --git a/waffle/tests/test_admin.py b/waffle/tests/test_admin.py index a22945b1..2855b94e 100755 --- a/waffle/tests/test_admin.py +++ b/waffle/tests/test_admin.py @@ -1,7 +1,4 @@ -try: - import mock -except ImportError: - import unittest.mock as mock +from unittest import mock import unittest import django diff --git a/waffle/tests/test_testutils.py b/waffle/tests/test_testutils.py index fd2d92ae..55456249 100644 --- a/waffle/tests/test_testutils.py +++ b/waffle/tests/test_testutils.py @@ -259,10 +259,10 @@ class OverrideSampleTransactionTestCase(OverrideSampleTestsMixin, TransactionTes """ -class OverrideSwitchOnClassTestsMixin(object): +class OverrideSwitchOnClassTestsMixin: @classmethod def setUpClass(cls): - super(OverrideSwitchOnClassTestsMixin, cls).setUpClass() + super().setUpClass() assert not waffle.get_waffle_switch_model().objects.filter(name='foo').exists() waffle.get_waffle_switch_model().objects.create(name='foo', active=True) @@ -286,10 +286,10 @@ class OverrideSwitchOnClassTransactionTestCase(OverrideSwitchOnClassTestsMixin, """ -class OverrideFlagOnClassTestsMixin(object): +class OverrideFlagOnClassTestsMixin: @classmethod def setUpClass(cls): - super(OverrideFlagOnClassTestsMixin, cls).setUpClass() + super().setUpClass() assert not waffle.get_waffle_flag_model().objects.filter(name='foo').exists() waffle.get_waffle_flag_model().objects.create(name='foo', everyone=True) @@ -313,10 +313,10 @@ class OverrideFlagOnClassTransactionTestCase(OverrideFlagOnClassTestsMixin, """ -class OverrideSampleOnClassTestsMixin(object): +class OverrideSampleOnClassTestsMixin: @classmethod def setUpClass(cls): - super(OverrideSampleOnClassTestsMixin, cls).setUpClass() + super().setUpClass() assert not waffle.get_waffle_sample_model().objects.filter(name='foo').exists() waffle.get_waffle_sample_model().objects.create(name='foo', percent='100.0') diff --git a/waffle/tests/test_waffle.py b/waffle/tests/test_waffle.py index 77f04739..87f3e69d 100644 --- a/waffle/tests/test_waffle.py +++ b/waffle/tests/test_waffle.py @@ -11,7 +11,7 @@ from django.test.utils import override_settings -import mock +from unittest import mock import waffle from test_app import views @@ -679,7 +679,7 @@ def test_pluggable_sample_model(self): assert waffle.sample_is_active('test_sample_on') -class TransactionTestMixin(object): +class TransactionTestMixin: """Mixin providing an abstract test case for writing in a transaction. """ def create_toggle(self): diff --git a/waffle/testutils.py b/waffle/testutils.py index 4b357df1..fba5adc7 100644 --- a/waffle/testutils.py +++ b/waffle/testutils.py @@ -13,7 +13,7 @@ class _overrider(TestContextDecorator): def __init__(self, name, active): - super(_overrider, self).__init__() + super().__init__() self.name = name self.active = active @@ -106,7 +106,7 @@ def update(self, active): else: p = active obj = self.cls.objects.get(pk=self.obj.pk) - obj.percent = '{0}'.format(p) + obj.percent = f'{p}' obj.save() obj.flush() From 6cc0c35bf9b6127a36c9fb0c25def33499137363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=9Clgen=20Sar=C4=B1kavak?= Date: Tue, 30 Aug 2022 17:55:41 +0300 Subject: [PATCH 4/6] Updated Django versions (#461) - Removed EOL Django versions - Added support for Django 4.1 --- setup.py | 4 +--- test_app/models.py | 7 +------ test_app/urls.py | 46 +++++++++++++++++++++------------------------- tox.ini | 10 ++++------ waffle/__init__.py | 3 --- 5 files changed, 27 insertions(+), 43 deletions(-) diff --git a/setup.py b/setup.py index 48423f2c..33b50a05 100644 --- a/setup.py +++ b/setup.py @@ -21,11 +21,9 @@ 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Framework :: Django', - 'Framework :: Django :: 2.2', - 'Framework :: Django :: 3.0', - 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.1', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', diff --git a/test_app/models.py b/test_app/models.py index 5003d1a3..884dce4a 100644 --- a/test_app/models.py +++ b/test_app/models.py @@ -1,12 +1,7 @@ from django.contrib.auth.base_user import AbstractBaseUser from django.db import models from django.db.models import CASCADE - -# ugettext variants removed in Django 4.0. Only gettext now available -try: - from django.utils.translation import ugettext_lazy as _ -except ImportError: - from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from waffle.models import AbstractBaseSample, AbstractBaseSwitch, AbstractUserFlag, CACHE_EMPTY from waffle.utils import get_setting, keyfmt, get_cache diff --git a/test_app/urls.py b/test_app/urls.py index 74fc7f56..01d43f40 100644 --- a/test_app/urls.py +++ b/test_app/urls.py @@ -1,8 +1,4 @@ -try: - from django.conf.urls import url -except ImportError: - from django.urls import re_path as url -from django.urls import include +from django.urls import include, path from django.contrib import admin from django.http import HttpResponseNotFound, HttpResponseServerError @@ -20,34 +16,34 @@ def handler500(r, exception=None): admin.autodiscover() urlpatterns = [ - url(r'^flag_in_view', views.flag_in_view, name='flag_in_view'), - url(r'^flag_in_view_readonly', views.flag_in_view_readonly, name='flag_in_view_readonly'), - url(r'^switch-on', views.switched_view), - url(r'^switch-off', views.switched_off_view), - url(r'^flag-on', views.flagged_view), - url(r'^foo_view', views.foo_view, name='foo_view'), - url(r'^foo_view_with_args/(?P\d+)/', views.foo_view_with_args, name='foo_view_with_args'), - url(r'^switched_view_with_valid_redirect', + path('flag_in_view', views.flag_in_view, name='flag_in_view'), + path('flag_in_view_readonly', views.flag_in_view_readonly, name='flag_in_view_readonly'), + path('switch-on', views.switched_view), + path('switch-off', views.switched_off_view), + path('flag-on', views.flagged_view), + path('foo_view', views.foo_view, name='foo_view'), + path('foo_view_with_args//', views.foo_view_with_args, name='foo_view_with_args'), + path('switched_view_with_valid_redirect', views.switched_view_with_valid_redirect), - url(r'^switched_view_with_valid_url_name', + path('switched_view_with_valid_url_name', views.switched_view_with_valid_url_name), - url(r'^switched_view_with_args_with_valid_redirect/(?P\d+)/', + path('switched_view_with_args_with_valid_redirect//', views.switched_view_with_args_with_valid_redirect), - url(r'^switched_view_with_args_with_valid_url_name/(?P\d+)/', + path('switched_view_with_args_with_valid_url_name//', views.switched_view_with_args_with_valid_url_name), - url(r'^switched_view_with_invalid_redirect', + path('switched_view_with_invalid_redirect', views.switched_view_with_invalid_redirect), - url(r'^flagged_view_with_valid_redirect', + path('flagged_view_with_valid_redirect', views.flagged_view_with_valid_redirect), - url(r'^flagged_view_with_valid_url_name', + path('flagged_view_with_valid_url_name', views.flagged_view_with_valid_url_name), - url(r'^flagged_view_with_args_with_valid_redirect/(?P\d+)/', + path('flagged_view_with_args_with_valid_redirect//', views.flagged_view_with_args_with_valid_redirect), - url(r'^flagged_view_with_args_with_valid_url_name/(?P\d+)/', + path('flagged_view_with_args_with_valid_url_name//', views.flagged_view_with_args_with_valid_url_name), - url(r'^flagged_view_with_invalid_redirect', + path('flagged_view_with_invalid_redirect', views.flagged_view_with_invalid_redirect), - url(r'^flag-off', views.flagged_off_view), - url(r'^', include('waffle.urls')), - url(r'^admin/', admin.site.urls), + path('flag-off', views.flagged_off_view), + path('', include('waffle.urls')), + path('admin/', admin.site.urls), ] diff --git a/tox.ini b/tox.ini index e774a712..28ef9c7f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{37,38,39}-django{22,30,31,32} - py{310}-django{32,40} + py{37,38,39,310}-django{32} + py{38,39,310}-django{40,41} [gh-actions] python = @@ -12,11 +12,9 @@ python = [testenv] deps = - django22: Django>=2.2,<2.3 - django30: Django>=3.0,<3.1 - django31: Django>=3.1,<3.2 django32: Django>=3.2,<3.3 django40: Django>=4.0,<4.1 + django41: Django>=4.1,<4.2 djangomain: https://github.com/django/django/archive/main.tar.gz -r{toxinidir}/requirements.txt commands = @@ -24,7 +22,7 @@ commands = [testenv:i18n] deps = - Django>=3.1,<3.3 + Django>=3.2,<4.2 -r{toxinidir}/requirements.txt commands = ./run.sh makemessages diff --git a/waffle/__init__.py b/waffle/__init__.py index 9cb65c9e..18a8498d 100755 --- a/waffle/__init__.py +++ b/waffle/__init__.py @@ -7,9 +7,6 @@ VERSION = (2, 7, 0) __version__ = '.'.join(map(str, VERSION)) -if django.VERSION < (3, 2): - default_app_config = 'waffle.apps.WaffleConfig' - def flag_is_active(request, flag_name, read_only=False): flag = get_waffle_flag_model().get(flag_name) From 4555aea9fecec77712501be18c65d0e0771e78a0 Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Tue, 30 Aug 2022 08:58:51 -0700 Subject: [PATCH 5/6] Releasing 3.0.0 (#462) --- docs/conf.py | 4 ++-- setup.py | 2 +- waffle/__init__.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 65953ffe..a272427d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,9 +46,9 @@ # built documents. # # The short X.Y version. -version = '2.7' +version = '3.0' # The full version, including alpha/beta/rc tags. -release = '2.7.0' +release = '3.0.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 33b50a05..bf73014d 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='django-waffle', - version='2.7.0', + version='3.0.0', description='A feature flipper for Django.', long_description=open('README.rst').read(), author='James Socol', diff --git a/waffle/__init__.py b/waffle/__init__.py index 18a8498d..e4b8535d 100755 --- a/waffle/__init__.py +++ b/waffle/__init__.py @@ -4,7 +4,7 @@ from waffle.utils import get_setting from django.apps import apps as django_apps -VERSION = (2, 7, 0) +VERSION = (3, 0, 0) __version__ = '.'.join(map(str, VERSION)) From e821a86cb1c1f51ef7f9dc9f39654d3f414747fd Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Tue, 30 Aug 2022 10:19:10 -0700 Subject: [PATCH 6/6] Fixed bug in Github deployment workflow --- .github/workflows/python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 11846a0a..24b21f0b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -98,7 +98,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 - - uses: casperdcl/deploy-pypi@v3 + - uses: casperdcl/deploy-pypi@v2 with: password: ${{ secrets.PYPI_API_TOKEN }} build: true