From aedb9523ebfc0e4cfdb9e78ff7caec3e2c8459ed Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 28 Jan 2021 18:51:37 +0100 Subject: [PATCH 1/6] Remove unneeded test mixin. --- tests/test_admin.py | 4 +-- tests/utils.py | 64 +++------------------------------------------ 2 files changed, 4 insertions(+), 64 deletions(-) diff --git a/tests/test_admin.py b/tests/test_admin.py index 38431594..6ae80fbf 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -13,8 +13,6 @@ from newsletter.admin_utils import make_subscription from newsletter.models import Message, Newsletter, Submission, Subscription, Attachment, attachment_upload_to -from .utils import AssertLogsMixin - test_files_dir = os.path.join(os.path.dirname(__file__), 'files') @@ -42,7 +40,7 @@ def setUp(self): message=self.message_with_attachment) -class AdminTestCase(AdminTestMixin, AssertLogsMixin, TestCase): +class AdminTestCase(AdminTestMixin, TestCase): def admin_import_file(self, source_file, ignore_errors=''): """ Upload an address file for import to admin. """ diff --git a/tests/utils.py b/tests/utils.py index 450f921f..76dd8904 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -12,69 +12,11 @@ from django.test import TestCase - from django.template import loader, TemplateDoesNotExist from django_webtest import WebTest -class AssertLogsMixin: - """Mixin to enable assertLogs method in Python 2.7. - - patch_logger has similar functionality for Python 2.7, but is - removed in Django 3.0. This mixin reworks patch_logger so that - the assertLogs method can be used for any supported Django - version. - """ - # TODO: Remove use of mixin when Django 1.11 support dropped - def assertLogs(self, logger=None, level=None): - # Use assertLogs context manager if present - try: - from unittest.case import _AssertLogsContext - - return _AssertLogsContext(self, logger, level) - except ImportError: - # Fallback if Django version does not support assertLogs - import logging - from django.test.utils import patch_logger - - class PatchLoggerResponse: - """Object to mimic AssertLogsContext response.""" - def __init__(self, messages): - self.output = messages - - @contextmanager - def patch_logger(logger_name, log_level, log_kwargs=False): - """Replicating patch_logger functionality from Django 1.11. - - Cannot use original Django patch_logger because of how - it returns its response. Have copied and modified it - to return an object that mimics the assertLogs response. - """ - logger_response = PatchLoggerResponse([]) - - def replacement(msg, *args, **kwargs): - call = msg % args - logger_response.output.append((call, kwargs) if log_kwargs else call) - - logger = logging.getLogger(logger_name) - orig = getattr(logger, log_level) - setattr(logger, log_level, replacement) - - try: - yield logger_response - finally: - setattr(logger, log_level, orig) - - if len(logger_response.output) == 0: - raise self.failureException( - "no logs of level {} or higher triggered on {}".format( - log_level, logger_name - ) - ) - - return patch_logger(logger, level.lower()) - class WebTestCase(WebTest): def setUp(self): self.site = Site.objects.get_current() @@ -98,7 +40,7 @@ def assertInContext(self, response, variable, self.assertEqual(instance, value) -class MailTestCase(AssertLogsMixin, TestCase): +class MailTestCase(TestCase): def get_email_list(self, email): if email: return (email,) @@ -160,7 +102,7 @@ def assertEmailHasHeader(self, header, content=None, email=None): self.assertEqual(my_email.extra_headers[header], content) -class UserTestCase(AssertLogsMixin, TestCase): +class UserTestCase(TestCase): def setUp(self): super().setUp() @@ -185,7 +127,7 @@ def tearDown(self): self.user.delete() -class ComparingTestCase(AssertLogsMixin, TestCase): +class ComparingTestCase(TestCase): def assertLessThan(self, value1, value2): self.assertTrue(value1 < value2) From 5808e44b444bd7a925b86cebe65807f7d2453a37 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 28 Jan 2021 18:53:36 +0100 Subject: [PATCH 2/6] Revamp some of the packaging files. - Stop using test_suite in setup.py. - Get rid of the requirements files. - Add tox.ini. --- .landscape.yml | 2 - Pipfile | 24 -- Pipfile.lock | 451 -------------------------------------- docs/conf.py | 12 +- newsletter/__init__.py | 7 + newsletter/admin_utils.py | 6 +- requirements.txt | 6 - requirements_test.txt | 8 - runtests.py | 39 ---- setup.py | 27 +-- tests/test_web.py | 4 +- tox.ini | 43 ++++ 12 files changed, 73 insertions(+), 556 deletions(-) delete mode 100644 .landscape.yml delete mode 100644 Pipfile delete mode 100644 Pipfile.lock delete mode 100644 requirements.txt delete mode 100644 requirements_test.txt delete mode 100755 runtests.py create mode 100644 tox.ini diff --git a/.landscape.yml b/.landscape.yml deleted file mode 100644 index ed35b527..00000000 --- a/.landscape.yml +++ /dev/null @@ -1,2 +0,0 @@ -ignore-paths: - - newsletter/south_migrations diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 7ed6d44c..00000000 --- a/Pipfile +++ /dev/null @@ -1,24 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[packages] -django-imperavi = "*" -django-tinymce = "*" -pytz = "*" -django-webtest = "*" -WebTest = "*" -python-card-me = "<1.0" -ldif3 = "<3.2" -chardet = "*" -surlex = ">=0.2.0" -sorl-thumbnail = ">=12.6.3" -unicodecsv = "<0.15" -Django = ">=2.2.16" -Pillow = ">=6.2.2" - -[dev-packages] -coverage = "*" -transifex-client = "*" -twine = "*" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 5506ad91..00000000 --- a/Pipfile.lock +++ /dev/null @@ -1,451 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "c88397d77e5b23467f123b4884212deafc9bc3022cbf2f4b96d7c7fa33b546b4" - }, - "pipfile-spec": 6, - "requires": {}, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "asgiref": { - "hashes": [ - "sha256:7e51911ee147dd685c3c8b805c0ad0cb58d360987b56953878f8c06d2d1c6f1a", - "sha256:9fc6fb5d39b8af147ba40765234fa822b39818b12cc80b35ad9b0cef3a476aed" - ], - "version": "==3.2.10" - }, - "beautifulsoup4": { - "hashes": [ - "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35", - "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25", - "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666" - ], - "version": "==4.9.3" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "index": "pypi", - "version": "==3.0.4" - }, - "django": { - "hashes": [ - "sha256:a2127ad0150ec6966655bedf15dbbff9697cc86d61653db2da1afa506c0b04cc", - "sha256:c93c28ccf1d094cbd00d860e83128a39e45d2c571d3b54361713aaaf9a94cac4" - ], - "index": "pypi", - "version": "==3.1.2" - }, - "django-imperavi": { - "hashes": [ - "sha256:2b05ff0afbc8a23a28ab599abef6aa8e762f32b5830ce7522905b471763d3da2" - ], - "index": "pypi", - "version": "==0.2.3" - }, - "django-tinymce": { - "hashes": [ - "sha256:47db20515d159c69e3b8c69dca73adfb4e60010335250514d9f6fce6dc98c85c", - "sha256:d3a641ec0d10db05dacab7bf9b11e9c8114d017ad1a9132ba18b10ab6bff1934" - ], - "index": "pypi", - "version": "==3.1.0" - }, - "django-webtest": { - "hashes": [ - "sha256:b9b4b94670c0ce533efc456d02dd55a0d0a7a8f7912eb30728dca2d59d7948b4", - "sha256:c5a1e486a3d8d3623aa615b6db2f27de848aa7079303a84721e9a685f839796c" - ], - "index": "pypi", - "version": "==1.9.7" - }, - "ldif3": { - "hashes": [ - "sha256:ccdf6ac2ed3f88912b7509529694c0e289c4392d34f2095cca22fa6d70ac189a" - ], - "index": "pypi", - "version": "==3.1.1" - }, - "mock": { - "hashes": [ - "sha256:3f9b2c0196c60d21838f307f5825a7b86b678cedc58ab9e50a8988187b4d81e0", - "sha256:dd33eb70232b6118298d516bbcecd26704689c386594f0f3c4f13867b2c56f72" - ], - "index": "pypi", - "version": "==4.0.2" - }, - "pillow": { - "hashes": [ - "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a", - "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae", - "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce", - "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e", - "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140", - "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb", - "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021", - "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6", - "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302", - "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c", - "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271", - "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09", - "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3", - "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015", - "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3", - "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544", - "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8", - "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792", - "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0", - "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3", - "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8", - "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11", - "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7", - "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11", - "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e", - "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039", - "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5", - "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72" - ], - "index": "pypi", - "version": "==8.0.1" - }, - "python-card-me": { - "hashes": [ - "sha256:37ca483a574534177227152123b1e1381330c75a62c4d4d60cd2eeb188384bf2" - ], - "index": "pypi", - "version": "==0.9.3" - }, - "python-dateutil": { - "hashes": [ - "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", - "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" - ], - "version": "==2.8.1" - }, - "pytz": { - "hashes": [ - "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", - "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" - ], - "index": "pypi", - "version": "==2020.1" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "index": "pypi", - "version": "==1.15.0" - }, - "sorl-thumbnail": { - "hashes": [ - "sha256:66771521f3c0ed771e1ce8e1aaf1639ebff18f7f5a40cfd3083da8f0fe6c7c99", - "sha256:7162639057dff222a651bacbdb6bd6f558fc32946531d541fc71e10c0167ebdf" - ], - "index": "pypi", - "version": "==12.6.3" - }, - "soupsieve": { - "hashes": [ - "sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55", - "sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232" - ], - "markers": "python_version >= '3.0'", - "version": "==2.0.1" - }, - "sqlparse": { - "hashes": [ - "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", - "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" - ], - "version": "==0.4.1" - }, - "surlex": { - "hashes": [ - "sha256:62057b52d147bf83aec36205a42a0080e0a0de4d6c8b8d6ae56adf477253b2df" - ], - "index": "pypi", - "version": "==0.2.0" - }, - "unicodecsv": { - "hashes": [ - "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc" - ], - "index": "pypi", - "version": "==0.14.1" - }, - "waitress": { - "hashes": [ - "sha256:1bb436508a7487ac6cb097ae7a7fe5413aefca610550baf58f0940e51ecfb261", - "sha256:3d633e78149eb83b60a07dfabb35579c29aac2d24bb803c18b26fb2ab1a584db" - ], - "version": "==1.4.4" - }, - "webob": { - "hashes": [ - "sha256:a3c89a8e9ba0aeb17382836cdb73c516d0ecf6630ec40ec28288f3ed459ce87b", - "sha256:aa3a917ed752ba3e0b242234b2a373f9c4e2a75d35291dcbe977649bd21fd108" - ], - "version": "==1.8.6" - }, - "webtest": { - "hashes": [ - "sha256:44ddfe99b5eca4cf07675e7222c81dd624d22f9a26035d2b93dc8862dc1153c6", - "sha256:aac168b5b2b4f200af4e35867cf316712210e3d5db81c1cbdff38722647bb087" - ], - "index": "pypi", - "version": "==2.0.35" - } - }, - "develop": { - "bleach": { - "hashes": [ - "sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080", - "sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd" - ], - "version": "==3.2.1" - }, - "certifi": { - "hashes": [ - "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3", - "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41" - ], - "version": "==2020.6.20" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "index": "pypi", - "version": "==3.0.4" - }, - "colorama": { - "hashes": [ - "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b", - "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2" - ], - "version": "==0.4.4" - }, - "coverage": { - "hashes": [ - "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516", - "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259", - "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9", - "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097", - "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0", - "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f", - "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7", - "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c", - "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5", - "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7", - "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729", - "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978", - "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9", - "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f", - "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9", - "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822", - "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418", - "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82", - "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f", - "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d", - "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221", - "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4", - "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21", - "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709", - "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54", - "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d", - "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270", - "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24", - "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751", - "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a", - "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237", - "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7", - "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636", - "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8" - ], - "index": "pypi", - "version": "==5.3" - }, - "docutils": { - "hashes": [ - "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", - "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" - ], - "version": "==0.16" - }, - "gitdb": { - "hashes": [ - "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac", - "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9" - ], - "version": "==4.0.5" - }, - "gitpython": { - "hashes": [ - "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b", - "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8" - ], - "version": "==3.1.11" - }, - "idna": { - "hashes": [ - "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", - "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" - ], - "version": "==2.10" - }, - "importlib-metadata": { - "hashes": [ - "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da", - "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3" - ], - "markers": "python_version < '3.8'", - "version": "==2.0.0" - }, - "keyring": { - "hashes": [ - "sha256:4e34ea2fdec90c1c43d6610b5a5fafa1b9097db1802948e90caf5763974b8f8d", - "sha256:9aeadd006a852b78f4b4ef7c7556c2774d2432bbef8ee538a3e9089ac8b11466" - ], - "version": "==21.4.0" - }, - "packaging": { - "hashes": [ - "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8", - "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181" - ], - "version": "==20.4" - }, - "pkginfo": { - "hashes": [ - "sha256:78d032b5888ec06d7f9d18fbf8c0549a6a3477081b34cb769119a07183624fc1", - "sha256:dd008e95b13141ddd05d7e8881f0c0366a998ab90b25c2db794a1714b71583cc" - ], - "version": "==1.6.0" - }, - "pygments": { - "hashes": [ - "sha256:381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0", - "sha256:88a0bbcd659fcb9573703957c6b9cff9fab7295e6e76db54c9d00ae42df32773" - ], - "version": "==2.7.2" - }, - "pyparsing": { - "hashes": [ - "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", - "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" - ], - "version": "==2.4.7" - }, - "python-slugify": { - "hashes": [ - "sha256:69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270" - ], - "version": "==4.0.1" - }, - "readme-renderer": { - "hashes": [ - "sha256:267854ac3b1530633c2394ead828afcd060fc273217c42ac36b6be9c42cd9a9d", - "sha256:6b7e5aa59210a40de72eb79931491eaf46fefca2952b9181268bd7c7c65c260a" - ], - "version": "==28.0" - }, - "requests": { - "hashes": [ - "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b", - "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898" - ], - "version": "==2.24.0" - }, - "requests-toolbelt": { - "hashes": [ - "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", - "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" - ], - "version": "==0.9.1" - }, - "rfc3986": { - "hashes": [ - "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d", - "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50" - ], - "version": "==1.4.0" - }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "index": "pypi", - "version": "==1.15.0" - }, - "smmap": { - "hashes": [ - "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4", - "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24" - ], - "version": "==3.0.4" - }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, - "tqdm": { - "hashes": [ - "sha256:43ca183da3367578ebf2f1c2e3111d51ea161ed1dc4e6345b86e27c2a93beff7", - "sha256:69dfa6714dee976e2425a9aab84b622675b7b1742873041e3db8a8e86132a4af" - ], - "version": "==4.50.2" - }, - "transifex-client": { - "hashes": [ - "sha256:a8a06330acb97403b24153fb51c2c6ae5c8ab0a989fed06f8a27ce70323c7d5e" - ], - "index": "pypi", - "version": "==0.14.1" - }, - "twine": { - "hashes": [ - "sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab", - "sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472" - ], - "index": "pypi", - "version": "==3.2.0" - }, - "urllib3": { - "hashes": [ - "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2", - "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e" - ], - "version": "==1.25.11" - }, - "webencodings": { - "hashes": [ - "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", - "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" - ], - "version": "==0.5.1" - }, - "zipp": { - "hashes": [ - "sha256:50a4ef266c31c9409627b46012e206382eb8d23f46fbd358065a8335cfbf7d8f", - "sha256:adf8f2ed8f614ced567d849cae9d183cef6cfd27c77a5cae7a28029be0c2b7a7" - ], - "version": "==3.3.2" - } - } -} diff --git a/docs/conf.py b/docs/conf.py index 45ffb757..0d70b417 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,7 +11,10 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os +import os +import sys +from pkg_resources import get_distribution + # Determine whether rendering on RTD on_rtd = os.environ.get('READTHEDOCS', None) == 'True' @@ -80,10 +83,9 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -version = '0.5' -# The full version, including alpha/beta/rc tags. -release = '0.5.1' +release = get_distribution('django-newsletter').version +# for example take major/minor +version = '.'.join(release.split('.')[:2]) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/newsletter/__init__.py b/newsletter/__init__.py index e69de29b..6b350941 100644 --- a/newsletter/__init__.py +++ b/newsletter/__init__.py @@ -0,0 +1,7 @@ +from pkg_resources import get_distribution, DistributionNotFound + +try: + __version__ = get_distribution("django-newsletter").version +except DistributionNotFound: + # package is not installed + __version__ = None diff --git a/newsletter/admin_utils.py b/newsletter/admin_utils.py index 18b9a7e5..51299075 100644 --- a/newsletter/admin_utils.py +++ b/newsletter/admin_utils.py @@ -2,7 +2,7 @@ from django.contrib.admin.utils import unquote from django.http import Http404 -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.translation import gettext as _ from .models import Subscription @@ -25,8 +25,8 @@ def _getobj(self, request, object_id): '%(name)s object with primary key ' '\'%(key)s\' does not exist.' ) % { - 'name': force_text(opts.verbose_name), - 'key': force_text(object_id) + 'name': force_str(opts.verbose_name), + 'key': force_str(object_id) } ) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index fd00206b..00000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -Django>=2.2.16 -python-card-me<1.0 -ldif3<3.2 -chardet -unicodecsv<0.15 -Pillow diff --git a/requirements_test.txt b/requirements_test.txt deleted file mode 100644 index 5aacc714..00000000 --- a/requirements_test.txt +++ /dev/null @@ -1,8 +0,0 @@ -django-imperavi -# TinyMCE above 3 doesn't support Python 3.5 anymore. -# TODO: Remove version freeze when Django 2.2 LTS support is dropped, early 2022. -django-tinymce<3 -pytz -webtest -django-webtest -mock diff --git a/runtests.py b/runtests.py deleted file mode 100755 index e000c087..00000000 --- a/runtests.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python - -import os -import sys - -import django -from django.core.management.commands.test import Command as TestCommand - - -def setup_django(): - """ Setup Django for testing using the test_project directory """ - - test_project_dir = os.path.join(os.path.dirname(__file__), 'test_project') - sys.path.insert(0, test_project_dir) - - os.environ['DJANGO_SETTINGS_MODULE'] = 'test_project.settings' - django.setup() - - -def run_tests(): - """ Run tests via setuptools, all tests with no special options """ - - setup_django() - - # Bypass argument parsing and run the test command manually with minimal args - TestCommand().handle(**{'testrunner': None, 'liveserver': None}) - - sys.exit(0) # TestCommand exits itself on failure, we only exit on success - - -if __name__ == "__main__": - setup_django() - - # Command expects to be called via 'manage.py test' so - # add the extra argument so that it can parse correctly - sys.argv.insert(1, 'test') - - # Run the test command with argv to get all the argument goodies - TestCommand().run_from_argv(sys.argv) diff --git a/setup.py b/setup.py index 07901186..52fc0da4 100755 --- a/setup.py +++ b/setup.py @@ -30,21 +30,11 @@ warnings.warn('Could not read README.rst and/or CHANGES.rst') README = None -try: - REQUIREMENTS = open('requirements.txt').read() -except: - warnings.warn('Could not read requirements.txt') - REQUIREMENTS = None - -try: - TEST_REQUIREMENTS = open('requirements_test.txt').read() -except: - warnings.warn('Could not read requirements_test.txt') - TEST_REQUIREMENTS = None setup( name='django-newsletter', - version="1.0b1", + use_scm_version={"version_scheme": "post-release"}, + setup_requires=["setuptools_scm"], description=( 'Django app for managing multiple mass-mailing lists with both ' 'plaintext as well as HTML templates (and pluggable WYSIWYG editors ' @@ -52,7 +42,14 @@ 'the admin interface.' ), long_description=README, - install_requires=REQUIREMENTS, + install_requires=[ + "Django>=2.2.16", + "python-card-me<1.0", + "ldif3<3.2", + "chardet", + "unicodecsv<0.15", + "Pillow", + ], author='Mathijs de Bruin', author_email='mathijs@mathijsfietst.nl', url='http://github.com/jazzband/django-newsletter/', @@ -66,12 +63,10 @@ 'License :: OSI Approved :: GNU Affero General Public License v3', 'Operating System :: OS Independent', 'Programming Language :: Python', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Topic :: Utilities' ], - test_suite='runtests.run_tests', - tests_require=TEST_REQUIREMENTS ) diff --git a/tests/test_web.py b/tests/test_web.py index 33fc058f..80fe25cf 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -9,7 +9,7 @@ from django.core import mail from django.contrib.auth import get_user_model from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.test.utils import override_settings from django.urls import reverse @@ -458,7 +458,7 @@ def test_unsubscribe_not_subscribed_view(self): self.assertIn( 'You are not subscribed to', - force_text(list(response.context['messages'])[0]) + force_str(list(response.context['messages'])[0]) ) def test_unsubscribe_post(self): diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..821aeafe --- /dev/null +++ b/tox.ini @@ -0,0 +1,43 @@ +[tox] +envlist = + py{36,37,38,39}-dj{22,30,31,dev} + +[testenv] +deps = + coverage + django-imperavi + # TinyMCE above 3 doesn't support Python 3.5 anymore. + # TODO: Remove version freeze when Django 2.2 LTS support is dropped, early 2022. + django-tinymce<3 + pytz + webtest + django-webtest + mock + dj22: Django>=2.2,<3.0 + dj30: Django>=3.0,<3.1 + dj31: Django>=3.1,<3.2 + djdev: https://github.com/django/django/archive/master.tar.gz +usedevelop = True +ignore_outcome = + djdev: True +commands = + coverage run {envbindir}/django-admin test + coverage report + coverage xml +setenv = + DJANGO_SETTINGS_MODULE=test_project.settings + PYTHONPATH={toxinidir}/test_project + +[gh-actions] +python = + 3.6: py36 + 3.7: py37 + 3.8: py38 + 3.9: py39 + +[gh-actions:env] +DJANGO = + 2.2: dj22 + 3.0: dj30 + 3.1: dj31 + dev: djdev From ce8f668516ffe2d8f3605ab93cac67d2c846932a Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 28 Jan 2021 18:54:04 +0100 Subject: [PATCH 3/6] Add GitHub Actions test workflow. --- .github/workflows/test.yml | 52 ++++++++++++++++++++++++++++++++++++++ .gitignore | 2 ++ .travis.yml | 41 ------------------------------ CONTRIBUTING.rst | 4 +-- README.rst | 11 ++++---- 5 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..ea9b9fd3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,52 @@ +name: Test + +on: [push, pull_request] + +jobs: + build: + name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + max-parallel: 5 + matrix: + python-version: ['3.6', '3.7', '3.8', '3.9'] + django-version: ['2.2', '3.0', '3.1', 'dev'] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Tox tests + run: | + tox -v + env: + DJANGO: ${{ matrix.django-version }} + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: Python ${{ matrix.python-version }} diff --git a/.gitignore b/.gitignore index bea050bb..089a1a33 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ django_setuptest* docs/_* .idea/ venv/ +coverage.xml +.tox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4dae500f..00000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -os: linux - -language: python - -python: - - 3.5 - - 3.6 - - 3.7 - - 3.8 - -env: - - DJANGO="Django<2.3" # Django 2.2.x LTS - - DJANGO="Django<3.1" # Django 3.0.x - - DJANGO="Django<3.2" # Django 3.1.x - -cache: - directories: - - $HOME/.cache/pip - -matrix: - exclude: - # Django 3.0 and 3.1 don't support Python 3.5 - - env: DJANGO="Django<3.1" - python: 3.5 - - env: DJANGO="Django<3.2" - python: 3.5 - -# Command to install dependencies -install: - # Latest PIP uses wheel by default - - pip install --upgrade pip - - pip install $DJANGO - - pip install -r requirements.txt - - pip install -r requirements_test.txt - - pip install coveralls - -# Command to run tests -script: coverage run setup.py test - -after_success: - coveralls diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index af7b03e8..142f8db2 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -13,6 +13,6 @@ Thanks for your awesome contrib! If you'd like to have your code included in mas couple of things you have to take care of though: 1. Ensure that the way you implemented the functionality is generic enough for other users to make use of and does not degrade the performance of existing users. If you unsure about this, create an issue with proposed functionality to discuss with the collaborators first. -2. Make sure the tests are passing. In any case, Travis should report passing tests. -3. Extended tests to cover any additional code included in your commit. In any case, the coveralls report should report equal or increased coverage. +2. Make sure the tests are passing. In any case, GitHub Actions should report passing tests. +3. Extended tests to cover any additional code included in your commit. In any case, the Codecov report should report equal or increased coverage. 4. Make sure that any added or changed functionality is documented in the Sphinx documentation. diff --git a/README.rst b/README.rst index 87d90c4d..d7582832 100644 --- a/README.rst +++ b/README.rst @@ -5,11 +5,12 @@ django-newsletter .. image:: https://img.shields.io/pypi/v/django-newsletter.svg :target: https://pypi.python.org/pypi/django-newsletter -.. image:: https://img.shields.io/travis/jazzband/django-newsletter/master.svg - :target: http://travis-ci.org/jazzband/django-newsletter +.. image:: https://github.com/jazzband/django-newsletter/workflows/Test/badge.svg + :target: https://github.com/jazzband/django-newsletter/actions + :alt: GitHub Actions -.. image:: https://coveralls.io/repos/github/jazzband/django-newsletter/badge.svg?branch=master - :target: https://coveralls.io/github/jazzband/django-newsletter?branch=master +.. image:: https://codecov.io/gh/jazzband/django-newsletter/branch/master/graph/badge.svg + :target: https://codecov.io/gh/jazzband/django-newsletter .. image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ @@ -58,7 +59,7 @@ Fairly extensive tests are available for internal frameworks, web (un)subscription and mail sending. Sending a newsletter to large groups of recipients (+15k) has been confirmed to work in multiple production environments. Tests for pull req's and the master branch are automatically run through -`Travis CI `_. +`GitHub Actions `_. Contributing ============= From d89695a696d945587879a726a28f3b53354ef0dc Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 28 Jan 2021 18:54:57 +0100 Subject: [PATCH 4/6] Add release workflow. --- .github/workflows/release.yml | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..fb049935 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,53 @@ +name: Release + +on: + push: + tags: + - '*' + +jobs: + build: + if: github.repository == 'jazzband/django-newsletter' + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: release-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + release- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U setuptools twine wheel + + - name: Build package + run: | + python setup.py --version + python setup.py sdist --format=gztar bdist_wheel + twine check dist/* + + - name: Upload packages to Jazzband + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@master + with: + user: jazzband + password: ${{ secrets.JAZZBAND_RELEASE_KEY }} + repository_url: https://jazzband.co/projects/django-newsletter/upload From fe6662cf1d3e3633293b9d56c63b140ac198631c Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Wed, 10 Mar 2021 13:38:15 +0100 Subject: [PATCH 5/6] Update setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 52fc0da4..de773439 100755 --- a/setup.py +++ b/setup.py @@ -67,6 +67,7 @@ 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Utilities' ], ) From 15adbe32fa0785258d116342ce497961517813e9 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Wed, 10 Mar 2021 13:41:41 +0100 Subject: [PATCH 6/6] Rename Django's dev branch to main. --- .github/workflows/test.yml | 2 +- tox.ini | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea9b9fd3..6765bee9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: max-parallel: 5 matrix: python-version: ['3.6', '3.7', '3.8', '3.9'] - django-version: ['2.2', '3.0', '3.1', 'dev'] + django-version: ['2.2', '3.0', '3.1', 'main'] steps: - uses: actions/checkout@v2 diff --git a/tox.ini b/tox.ini index 821aeafe..b32407cc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] envlist = - py{36,37,38,39}-dj{22,30,31,dev} + py{36,37,38,39}-dj{22,30,31} + py{38,39}-djmain [testenv] deps = @@ -16,10 +17,10 @@ deps = dj22: Django>=2.2,<3.0 dj30: Django>=3.0,<3.1 dj31: Django>=3.1,<3.2 - djdev: https://github.com/django/django/archive/master.tar.gz + djmain: https://github.com/django/django/archive/main.tar.gz usedevelop = True ignore_outcome = - djdev: True + djmain: True commands = coverage run {envbindir}/django-admin test coverage report @@ -40,4 +41,4 @@ DJANGO = 2.2: dj22 3.0: dj30 3.1: dj31 - dev: djdev + main: djmain