diff --git a/.flake8 b/.flake8 deleted file mode 100644 index eceea15..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -# Ignore "and" at start of line. -ignore = W503 -max-line-length = 120 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..89189ce --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,37 @@ +name: Check + +on: + - push + - pull_request + +jobs: + build: + name: ${{ matrix.tox-environment }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + tox-environment: + - docs + - lint + + env: + TOXENV: ${{ matrix.tox-environment }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + + - name: Set up system dependencies + run: | + sudo apt-get update + sudo apt-get install gettext + + - name: Install dependencies + run: python -m pip install tox + + - name: Run + run: tox diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..e0c894e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,47 @@ +name: Test + +on: + - push + - pull_request + +jobs: + build: + name: Python ${{ matrix.python-version }} / ${{ matrix.tox-environment }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + python-version: + - 3.6 + - 3.7 + - 3.8 + - 3.9 + - pypy3 + tox-environment: + # Previous LTS + - django22 + # Latest release + - django32 + + env: + TOXENV: ${{ matrix.tox-environment }} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Set up system dependencies + run: | + sudo apt-get update + sudo apt-get install gettext + + - name: Install dependencies + run: python -m pip install tox + + - name: Run tests + run: tox diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a0f1b84..0000000 --- a/.travis.yml +++ /dev/null @@ -1,39 +0,0 @@ -sudo: false -language: python -cache: pip - -script: - - tox - -install: - - pip install tox - -matrix: - include: - # LTS - - python: "2.7" - env: TOXENV=py27-django111 - - python: "3.4" - env: TOXENV=py34-django111 - - # Latest - - python: "3.4" - env: TOXENV=py34-django20 - - python: "3.5" - env: TOXENV=py35-django20 - - python: "3.6" - env: TOXENV=py36-django20 - - # Pypy - - python: "pypy" - env: TOXENV=pypy2-django111 - - python: "pypy3" - env: TOXENV=pypy3-django20 - - # Linting - - python: "3.6" - env: TOXENV=lint - -notifications: - email: false - irc: "irc.freenode.org#xelnext" diff --git a/LICENSE b/LICENSE index a6674bd..f860da6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2013, Raphaël Barrois +Copyright (c) 2011-2020, Raphaël Barrois All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/MANIFEST.in b/MANIFEST.in index f138b06..8465f7e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,14 +1,15 @@ include CREDITS ChangeLog LICENSE README.rst -include requirements*.txt # Required for 'setup.py build' include Makefile graft django_xworkflows -prune dev -prune docs -prune tests +graft dev +graft docs +graft tests + +prune docs/_build global-exclude *.py[cod] __pycache__ -exclude tox.ini .flake8 manage.py +include tox.ini .flake8 manage.py diff --git a/Makefile b/Makefile index 9885548..6723111 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,6 @@ DOC_DIR=docs # Use current python binary instead of system default. COVERAGE = python $(shell which coverage) FLAKE8 = flake8 -MANAGE_PY = python manage.py DJANGO_ADMIN = django-admin.py PO_FILES = $(shell find $(PACKAGE) -name '*.po') MO_FILES = $(PO_FILES:.po=.mo) @@ -36,9 +35,9 @@ build: $(MO_FILES) cd $(abspath $(dir $<)/../../..) && $(DJANGO_ADMIN) compilemessages # DOC: Install and/or upgrade dependencies -update: +setup-dev: pip install --upgrade pip setuptools - pip install --upgrade -r requirements_dev.txt + pip install --upgrade -e .[dev,doc] pip freeze @@ -46,7 +45,7 @@ release: fullrelease -.PHONY: all default clean build update release +.PHONY: all default clean build setup-dev release # Tests and quality @@ -59,16 +58,16 @@ testall: # DOC: Run tests for the currently installed version test: build - PYTHONPATH=. python -Wdefault $(TESTS_DIR)/runner.py + python -Werror ./manage.py test tests.djworkflows.tests # Note: we run the linter in two runs, because our __init__.py files has specific warnings we want to exclude # DOC: Perform code quality tasks lint: - $(FLAKE8) --config .flake8 --exclude $(PACKAGE)/__init__.py $(PACKAGE) - $(FLAKE8) --config .flake8 --ignore F401 $(PACKAGE)/__init__.py - $(FLAKE8) --config .flake8 $(TESTS_DIR) + $(FLAKE8) --exclude $(PACKAGE)/__init__.py $(PACKAGE) + $(FLAKE8) --ignore F401 $(PACKAGE)/__init__.py + $(FLAKE8) $(TESTS_DIR) check-manifest coverage: diff --git a/dev/settings.py b/dev/settings.py index 3259098..7c5af89 100644 --- a/dev/settings.py +++ b/dev/settings.py @@ -12,6 +12,8 @@ } } +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. @@ -104,7 +106,7 @@ }, ] -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -112,12 +114,14 @@ 'django.contrib.messages.middleware.MessageMiddleware', ) -ROOT_URLCONF = 'migration_helper.urls' +ROOT_URLCONF = 'dev.urls' INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sites', + 'tests.djworkflows', + 'django_xworkflows', 'django_xworkflows.xworkflow_log', ) diff --git a/dev/urls.py b/dev/urls.py index 77c95cd..fb19b8d 100644 --- a/dev/urls.py +++ b/dev/urls.py @@ -1,17 +1,6 @@ -from django.conf.urls.defaults import patterns, include, url # Uncomment the next two lines to enable the admin: # from django.contrib import admin # admin.autodiscover() -urlpatterns = patterns('', - # Examples: - # url(r'^$', 'migration_helper.views.home', name='home'), - # url(r'^migration_helper/', include('migration_helper.foo.urls')), - - # Uncomment the admin/doc line below to enable admin documentation: - # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), - - # Uncomment the next line to enable the admin: - # url(r'^admin/', include(admin.site.urls)), -) +urlpatterns = [] diff --git a/django_xworkflows/__init__.py b/django_xworkflows/__init__.py index d04b059..ee9e8a5 100644 --- a/django_xworkflows/__init__.py +++ b/django_xworkflows/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -__version__ = '0.12.4.dev0' +__version__ = '1.0.1.dev0' __author__ = 'Raphaël Barrois ' diff --git a/django_xworkflows/compat.py b/django_xworkflows/compat.py deleted file mode 100644 index f30d749..0000000 --- a/django_xworkflows/compat.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois -# This code is distributed under the two-clause BSD license. - -from __future__ import unicode_literals - -"""Compatibility helpers.""" - - -import sys - -is_python2 = (sys.version_info[0] == 2) diff --git a/django_xworkflows/locale/fr_FR/LC_MESSAGES/django.po b/django_xworkflows/locale/fr_FR/LC_MESSAGES/django.po index 99b305f..edad0ca 100644 --- a/django_xworkflows/locale/fr_FR/LC_MESSAGES/django.po +++ b/django_xworkflows/locale/fr_FR/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # Locale for django-xworkflows. -# Copyright (C) 2011 Raphaël Barrois +# Copyright (C) 2011-2020 Raphaël Barrois # This file is distributed under the same license as the django-xworkflows package. # Raphaël Barrois 2011. # diff --git a/django_xworkflows/management/commands/rebuild_transitionlog_states.py b/django_xworkflows/management/commands/rebuild_transitionlog_states.py index 513efe7..73a134b 100644 --- a/django_xworkflows/management/commands/rebuild_transitionlog_states.py +++ b/django_xworkflows/management/commands/rebuild_transitionlog_states.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -from __future__ import unicode_literals - """Rebuild missing from_state/to_state fields on TransitionLog objects.""" diff --git a/django_xworkflows/models.py b/django_xworkflows/models.py index 423f859..a020923 100644 --- a/django_xworkflows/models.py +++ b/django_xworkflows/models.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -from __future__ import unicode_literals - """Specific versions of XWorkflows to use with Django.""" from django.apps import apps @@ -16,9 +14,8 @@ from django.forms import fields from django.forms import widgets from django.utils.deconstruct import deconstructible -from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from xworkflows import base @@ -249,7 +246,7 @@ class BaseWorkflowEnabled(base.BaseWorkflowEnabled): def _get_FIELD_display(self, field): if isinstance(field, StateField): value = getattr(self, field.attname) - return force_text(value.title) + return str(value.title) else: return super(BaseWorkflowEnabled, self)._get_FIELD_display(field) @@ -292,9 +289,7 @@ def get_default_log_model(): class DjangoImplementationWrapper(base.ImplementationWrapper): """Restrict execution of transitions within templates""" - # django < 1.4 alters_data = True - # django >= 1.4 do_not_call_in_templates = True @@ -423,7 +418,6 @@ def log_transition(self, transition, from_state, instance, *args, **kwargs): self.db_log(transition, from_state, instance, *args, **kwargs) -@python_2_unicode_compatible class BaseTransitionLog(models.Model): """Abstract model for a minimal database logging setup. diff --git a/django_xworkflows/xworkflow_log/admin.py b/django_xworkflows/xworkflow_log/admin.py index 9057ba3..e0d43d4 100644 --- a/django_xworkflows/xworkflow_log/admin.py +++ b/django_xworkflows/xworkflow_log/admin.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -from __future__ import unicode_literals - from . import models from django.contrib import admin diff --git a/django_xworkflows/xworkflow_log/locale/fr_FR/LC_MESSAGES/django.po b/django_xworkflows/xworkflow_log/locale/fr_FR/LC_MESSAGES/django.po index 4294120..1952832 100644 --- a/django_xworkflows/xworkflow_log/locale/fr_FR/LC_MESSAGES/django.po +++ b/django_xworkflows/xworkflow_log/locale/fr_FR/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # Locale for django-xworkflows. -# Copyright (C) 2011 Raphaël Barrois +# Copyright (C) 2011-2020 Raphaël Barrois # This file is distributed under the same license as the django-xworkflows package. # Raphaël Barrois 2011. # diff --git a/django_xworkflows/xworkflow_log/migrations/0001_initial.py b/django_xworkflows/xworkflow_log/migrations/0001_initial.py index 328ca9b..ee62e2b 100644 --- a/django_xworkflows/xworkflow_log/migrations/0001_initial.py +++ b/django_xworkflows/xworkflow_log/migrations/0001_initial.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # flake8: noqa -from __future__ import unicode_literals from django.db import models, migrations import django.utils.timezone @@ -12,7 +11,7 @@ class Migration(migrations.Migration): dependencies = [ - ('contenttypes', '0001_initial'), + ('contenttypes', '0002_remove_content_type_name'), migrations.swappable_dependency(XWORKFLOWS_USER_MODEL), ] diff --git a/django_xworkflows/xworkflow_log/models.py b/django_xworkflows/xworkflow_log/models.py index 50068ec..55c56f2 100644 --- a/django_xworkflows/xworkflow_log/models.py +++ b/django_xworkflows/xworkflow_log/models.py @@ -1,12 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -from __future__ import unicode_literals - from django.conf import settings from django.db import models as django_models -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from .. import models diff --git a/docs/changelog.rst b/docs/changelog.rst index 5146250..71e3441 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,10 +1,22 @@ ChangeLog ========= -0.12.4 (unreleased) -------------------- +1.0.1 (unreleased) +------------------ + +*New:* + + - Test against Django 3.1, 3.2 + - Test against Python 3.8, 3.9 + + +1.0.0 (2020-03-09) +------------------ + +*New:* -- Nothing changed yet. + - Add compatibility for Django up to 3.0 + - Drop support for Python 2 0.12.3 (2018-02-06) diff --git a/docs/conf.py b/docs/conf.py index 7f6e1bb..d830741 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,8 +11,6 @@ # All configuration values have a default; values that are commented out # serve to show the default. -from __future__ import unicode_literals - import sys, os # If extensions (or modules to document with autodoc) are in another directory, @@ -43,7 +41,7 @@ # General information about the project. project = 'django-xworkflows' -copyright = '2011-2013, Raphaël Barrois' +copyright = '2011-2020, Raphaël Barrois' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/index.rst b/docs/index.rst index f8e7835..6c5a09c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,7 @@ django-xworkflows documentation =============================== -django-xworkflows is a django application adding `xworkflows `_ functionnalities to +django-xworkflows is a django application adding `xworkflows `_ functionalities to django models. diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e18794f..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -# No hard external requirements. diff --git a/requirements_dev.txt b/requirements_dev.txt deleted file mode 100644 index bb81b69..0000000 --- a/requirements_dev.txt +++ /dev/null @@ -1,11 +0,0 @@ -# Requirements for local development --e . --r requirements_test.txt - -coverage -wheel -tox -zest.releaser[recommended] - -Sphinx -sphinx_rtd_theme diff --git a/requirements_test.txt b/requirements_test.txt deleted file mode 100644 index 35012df..0000000 --- a/requirements_test.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Common requirements for running tests -check_manifest -flake8 diff --git a/setup.cfg b/setup.cfg index 2d859ae..8eab186 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,60 @@ +[metadata] +name = django_xworkflows +version = 1.0.1.dev0 +description = A Django app enabling Django models to use xworkflows. +long_description = file: README.rst +# https://docutils.sourceforge.io/FAQ.html#what-s-the-official-mime-type-for-restructuredtext-data +long_description_content_type = text/x-rst +author = Raphaël Barrois +author_email = raphael.barrois+django_xworkflows@polytechnique.org +url = https://github.com/rbarrois/django_xworkflows +keywords = django, workflow, state machine, automaton +license = BSD +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: BSD License + Topic :: Software Development :: Libraries :: Python Modules + Operating System :: OS Independent + Framework :: Django :: 2.2 + Framework :: Django :: 3.0 + Framework :: Django :: 3.1 + Framework :: Django :: 3.2 + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + +[options] +zip_safe = false +packages = find: +include_package_data = true +python_requires = >=3.4 +install_requires = + Django>=1.11 + xworkflows + +[options.packages.find] +exclude = + dev + tests* + +[options.extras_require] +dev = + # Testing + coverage + check_manifest + flake8 + tox + # Packaging + wheel + zest.releaser[recommended] +doc = + Sphinx + sphinx_rtd_theme + [bdist_wheel] universal = true @@ -10,3 +67,8 @@ python-file-with-version = django_xworkflows/__init__.py [distutils] index-servers = pypi + +[flake8] +# Ignore "and" at start of line. +ignore = W503 +max-line-length = 120 diff --git a/setup.py b/setup.py index 6bc8210..ef40c3d 100644 --- a/setup.py +++ b/setup.py @@ -1,91 +1,24 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -import codecs -import os -import re import subprocess -import sys -from setuptools import setup, find_packages +from setuptools import setup from setuptools.command import build_py -root_dir = os.path.abspath(os.path.dirname(__file__)) - - -def get_version(package_name): - version_re = re.compile(r"^__version__ = [\"']([\w_.-]+)[\"']$") - package_components = package_name.split('.') - init_path = os.path.join(root_dir, *(package_components + ['__init__.py'])) - with codecs.open(init_path, 'r', 'utf-8') as f: - for line in f: - match = version_re.match(line[:-1]) - if match: - return match.groups()[0] - return '0.1.0' - - -def clean_readme(fname): - """Cleanup README.rst for proper PyPI formatting.""" - with codecs.open(fname, 'r', 'utf-8') as f: - return ''.join( - re.sub(r':\w+:`([^`]+?)( <[^<>]+>)?`', r'``\1``', line) - for line in f - if not (line.startswith('.. currentmodule') or line.startswith('.. toctree')) - ) - class BuildWithMakefile(build_py.build_py): """Custom 'build' command that runs 'make build' first.""" def run(self): subprocess.check_call(['make', 'build']) - if sys.version_info[0] < 3: - # Under Python 2.x, build_py is an old-style class. - return build_py.build_py.run(self) return super().run() -PACKAGE = 'django_xworkflows' - - setup( - name=PACKAGE, - version=get_version(PACKAGE), - author="Raphaël Barrois", - author_email="raphael.barrois+%s@polytechnique.org" % PACKAGE, - description="A django app enabling Django models to use xworkflows.", - long_description=clean_readme('README.rst'), - license="BSD", - keywords="django workflow state machine automaton", - url="http://github.com/rbarrois/django_xworkflows", - download_url="http://pypi.python.org/pypi/django-xworkflows/", - packages=find_packages(exclude=['dev', 'tests*']), - cmdclass={'build_py': BuildWithMakefile}, - include_package_data=True, - setup_requires=[ - 'setuptools>=0.8', - ], - install_requires=[ - 'Django', - 'xworkflows', - ], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", - "Topic :: Software Development :: Libraries :: Python Modules", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - ], - test_suite='tests.runner.runtests', - zip_safe=False, # Prevent distribution as eggs for South. + cmdclass=dict( + build_py=BuildWithMakefile, + ), ) diff --git a/tests/demo_project/demo_project/settings.py b/tests/demo_project/demo_project/settings.py index 4126ede..748f5fb 100644 --- a/tests/demo_project/demo_project/settings.py +++ b/tests/demo_project/demo_project/settings.py @@ -41,7 +41,7 @@ 'workflow_app', ) -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -83,6 +83,7 @@ } } +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # Internationalization # https://docs.djangoproject.com/en/1.8/topics/i18n/ diff --git a/tests/djworkflows/__init__.py b/tests/djworkflows/__init__.py index a84a884..68a0855 100644 --- a/tests/djworkflows/__init__.py +++ b/tests/djworkflows/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. diff --git a/tests/djworkflows/migrations/0001_initial.py b/tests/djworkflows/migrations/0001_initial.py new file mode 100644 index 0000000..e903e42 --- /dev/null +++ b/tests/djworkflows/migrations/0001_initial.py @@ -0,0 +1,117 @@ +# Generated by Django 3.0.4 on 2020-03-09 13:39 +# flake8: noqa + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import django_xworkflows.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + migrations.CreateModel( + name='GenericWorkflowEnabled', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('state', django_xworkflows.models.StateField(max_length=16, workflow=django_xworkflows.models._SerializedWorkflow(initial_state='a', name='GenericWorkflow', states=['a', 'b']))), + ], + options={ + 'abstract': False, + }, + bases=(django_xworkflows.models.BaseWorkflowEnabled, models.Model), + ), + migrations.CreateModel( + name='MyWorkflowEnabled', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('state', django_xworkflows.models.StateField(max_length=16, workflow=django_xworkflows.models._SerializedWorkflow(initial_state='foo', name='MyWorkflow', states=['foo', 'bar', 'baz']))), + ('other', models.CharField(choices=[('aaa', 'AAA'), ('bbb', 'BBB')], max_length=4)), + ], + options={ + 'abstract': False, + }, + bases=(django_xworkflows.models.BaseWorkflowEnabled, models.Model), + ), + migrations.CreateModel( + name='SomeWorkflowEnabled', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('state', django_xworkflows.models.StateField(max_length=16, workflow=django_xworkflows.models._SerializedWorkflow(initial_state='a', name='SomeWorkflow', states=['a', 'b']))), + ], + options={ + 'abstract': False, + }, + bases=(django_xworkflows.models.BaseWorkflowEnabled, models.Model), + ), + migrations.CreateModel( + name='WithTwoWorkflows', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('state1', django_xworkflows.models.StateField(max_length=16, workflow=django_xworkflows.models._SerializedWorkflow(initial_state='foo', name='MyWorkflow', states=['foo', 'bar', 'baz']))), + ('state2', django_xworkflows.models.StateField(max_length=19, workflow=django_xworkflows.models._SerializedWorkflow(initial_state='a', name='MyAltWorkflow', states=['a', 'b', 'c', 'something_very_long']))), + ], + options={ + 'abstract': False, + }, + bases=(django_xworkflows.models.BaseWorkflowEnabled, models.Model), + ), + migrations.CreateModel( + name='SomeWorkflowLastTransitionLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('transition', models.CharField(db_index=True, max_length=255, verbose_name='transition')), + ('from_state', models.CharField(db_index=True, max_length=255, verbose_name='from state')), + ('to_state', models.CharField(db_index=True, max_length=255, verbose_name='to state')), + ('timestamp', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='performed at')), + ('obj', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='djworkflows.SomeWorkflowEnabled')), + ], + options={ + 'verbose_name': 'XWorkflow last transition log', + 'verbose_name_plural': 'XWorkflow last transition logs', + 'abstract': False, + }, + ), + migrations.CreateModel( + name='GenericWorkflowTransitionLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('transition', models.CharField(db_index=True, max_length=255, verbose_name='transition')), + ('from_state', models.CharField(db_index=True, max_length=255, verbose_name='from state')), + ('to_state', models.CharField(db_index=True, max_length=255, verbose_name='to state')), + ('timestamp', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='performed at')), + ('content_id', models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='Content id')), + ('content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='Content type')), + ], + options={ + 'verbose_name': 'XWorkflow transition log', + 'verbose_name_plural': 'XWorkflow transition logs', + 'ordering': ('-timestamp', 'transition'), + 'abstract': False, + }, + ), + migrations.CreateModel( + name='GenericWorkflowLastTransitionLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('transition', models.CharField(db_index=True, max_length=255, verbose_name='transition')), + ('from_state', models.CharField(db_index=True, max_length=255, verbose_name='from state')), + ('to_state', models.CharField(db_index=True, max_length=255, verbose_name='to state')), + ('timestamp', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='performed at')), + ('content_id', models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='Content id')), + ('content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='last_transition_logs', to='contenttypes.ContentType', verbose_name='Content type')), + ], + options={ + 'verbose_name': 'XWorkflow last transition log', + 'verbose_name_plural': 'XWorkflow last transition logs', + 'abstract': False, + 'unique_together': {('content_type', 'content_id')}, + }, + ), + ] diff --git a/tests/djworkflows/migrations/__init__.py b/tests/djworkflows/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/djworkflows/models.py b/tests/djworkflows/models.py index 4424abf..4d15e1e 100644 --- a/tests/djworkflows/models.py +++ b/tests/djworkflows/models.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -from __future__ import unicode_literals - from django.db import models import xworkflows @@ -52,11 +50,12 @@ class MyWorkflowEnabled(dxmodels.WorkflowEnabled, models.Model): state = dxmodels.StateField(MyWorkflow) other = models.CharField(max_length=4, choices=OTHER_CHOICES) + @xworkflows.after_transition("gobaz") def fail_if_fortytwo(self, res, *args, **kwargs): if res == 42: raise ValueError() - @dxmodels.transition(after=fail_if_fortytwo) + @dxmodels.transition() def gobaz(self, foo, save=True): return foo * 2 diff --git a/tests/djworkflows/tests.py b/tests/djworkflows/tests.py index 50591e1..36cc9df 100644 --- a/tests/djworkflows/tests.py +++ b/tests/djworkflows/tests.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois +# Copyright (c) 2011-2020 Raphaël Barrois # This code is distributed under the two-clause BSD license. -from __future__ import unicode_literals - import contextlib import os import re @@ -28,14 +26,6 @@ from . import models -if sys.version_info[0] <= 2: - def text_type(text): - return unicode(text) # noqa: F821 -else: - def text_type(text): - return str(text) - - @contextlib.contextmanager def extra_pythonpath(pythonpath): old_path = os.environ.get('PYTHONPATH') @@ -250,7 +240,7 @@ def test_logging(self): self.assertEqual('foo', trlog.from_state) self.assertEqual('bar', trlog.to_state) - self.assertIn('foo -> bar', text_type(trlog)) + self.assertIn('foo -> bar', str(trlog)) def test_no_logging(self): """Tests disabled transition logs.""" @@ -410,9 +400,9 @@ def step_models(self, step): CONDITIONAL_STEP_LINE_RE = r'^.*# step:(\d+,)*{}(,\d+)*$'.format(step) with open(self.source_models, 'r') as src: lines = [ - l for l in src - if (re.match(CONDITIONAL_STEP_LINE_RE, l.rstrip()) - or not re.match(CONDITIONAL_LINE_RE, l.rstrip())) + line for line in src + if (re.match(CONDITIONAL_STEP_LINE_RE, line.rstrip()) + or not re.match(CONDITIONAL_LINE_RE, line.rstrip())) ] if self.debug: @@ -442,8 +432,8 @@ def test_makemigrations(self): class TemplateTestCase(test.TestCase): """Tests states and transitions behavior in templates.""" - uTrue = text_type(True) - uFalse = text_type(False) + uTrue = str(True) + uFalse = str(False) def setUp(self): self.obj = models.MyWorkflowEnabled() @@ -480,11 +470,11 @@ def test_django_magic(self): self.assertTrue(self.obj.foobar.do_not_call_in_templates) def test_transaction_attributes(self): - self.assertEqual(self.render_fragment("{{ obj.foobar|safe}}"), text_type(self.obj.foobar)) + self.assertEqual(self.render_fragment("{{ obj.foobar|safe}}"), str(self.obj.foobar)) self.assertEqual(self.render_fragment("{{ obj.foobar.is_available }}"), self.uTrue) self.assertEqual(models.MyWorkflow.states.foo, self.obj.state) - self.assertEqual(self.render_fragment("{{ obj.bazbar|safe}}"), text_type(self.obj.bazbar)) + self.assertEqual(self.render_fragment("{{ obj.bazbar|safe}}"), str(self.obj.bazbar)) self.assertEqual(self.render_fragment("{{ obj.bazbar.is_available }}"), self.uFalse) self.assertEqual(models.MyWorkflow.states.foo, self.obj.state) diff --git a/tests/runner.py b/tests/runner.py deleted file mode 100644 index 591ddcb..0000000 --- a/tests/runner.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (c) 2011-2013 Raphaël Barrois -# This code is distributed under the two-clause BSD license. - -from __future__ import unicode_literals - -import sys - -import django -from django.conf import settings -from django.test.runner import DiscoverRunner as DjangoTestSuiteRunner - - -if not settings.configured: - settings.configure( - DATABASES={ - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - } - }, - INSTALLED_APPS=[ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'tests.djworkflows', - 'django_xworkflows', - 'django_xworkflows.xworkflow_log', - ], - MIDDLEWARE_CLASSES=[], - TEMPLATES=[{ - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - }], - ) - -django.setup() - -default_test_args = 'tests.djworkflows.tests' - - -def runtests(*test_args): - if not test_args: - test_args = [default_test_args] - runner = DjangoTestSuiteRunner(failfast=False) - failures = runner.run_tests(test_args) - sys.exit(failures) - - -if __name__ == '__main__': - runtests(*sys.argv[1:]) diff --git a/tox.ini b/tox.ini index 236cfa3..3720d9c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,25 +1,32 @@ [tox] envlist = - py27-django111 - py{34,35,36}-django{111,20} - pypy2-django111 - pypy3-django{111,20} + py{36,37,38,39,py3}-django22 + py{36,37,38,39,py3}-django30 + py{36,37,38,39,py3}-django31 + py{36,37,38,39,py3}-django32 lint + docs toxworkdir = {env:TOX_WORKDIR:.tox} [testenv] +extras = dev deps = - -rrequirements_test.txt - django111: Django>=1.11,<1.12 - django20: Django>=2.0,<2.1 + django22: Django>=2.2,<2.3 + django30: Django>=3.0,<3.1 + django31: Django>=3.1,<3.2 + django32: Django>=3.2,<3.3 whitelist_externals = make commands = make test [testenv:lint] -deps = - -rrequirements_test.txt +extras = dev whitelist_externals = make commands = make lint + +[testenv:docs] +extras = doc +whitelist_externals = make +commands = make doc