Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Ruff for linting and formatting #156

Merged
merged 3 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ jobs:
fail-fast: false
matrix:
env:
- bandit
- flake8
- isort
- pylint
- lint
- format
- package
- docs
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install prerequisites
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install prerequisites
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ jobs:
- { django-version: '5.0', python-version: '3.8' }
- { django-version: '5.0', python-version: '3.9' }
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install prerequisites
Expand All @@ -55,8 +55,8 @@ jobs:
env:
TOXENV: behave-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install prerequisites
Expand Down
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Release History

**Features and Improvements**

- Use ruff for linting and code style; reformat code base
- Migrate packaging from ``setup.py`` to pure ``pyproject.toml``.
- Add instructions to measure test coverage to the documentation
- Cover Python 3.9 to 3.12 and Django 3.2, 4.x and 5.0, drop Python 3.5, 3.6 and Django 2.2 and 3.0 support
Expand Down
19 changes: 11 additions & 8 deletions behave_django/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@


class PatchedContext(Context):
"""Provides methods for Behave's ``context`` variable."""

@property
def base_url(self):
try:
return self.test.live_server_url
except AttributeError as err:
raise RuntimeError('Web browser automation is not available. '
'This scenario step can not be run with the '
'--simple or -S flag.') from err
msg = (
'Web browser automation is not available. '
'This scenario step can not be run with the --simple or -S flag.'
)
raise RuntimeError(msg) from err

def get_url(self, to=None, *args, **kwargs):
return self.base_url + (
resolve_url(to, *args, **kwargs) if to else '')
return self.base_url + (resolve_url(to, *args, **kwargs) if to else '')


def load_registered_fixtures(context):
Expand All @@ -28,7 +30,7 @@ def load_registered_fixtures(context):
# -- SELECT STEP REGISTRY:
# HINT: Newer behave versions use runner.step_registry
# to be able to support multiple runners, each with its own step_registry.
runner = context._runner # pylint: disable=protected-access
runner = context._runner
step_registry = getattr(runner, 'step_registry', None)
if not step_registry:
# -- BACKWARD-COMPATIBLE: Use module_step_registry
Expand All @@ -45,10 +47,11 @@ def load_registered_fixtures(context):

class BehaveHooksMixin:
"""
Provides methods that run during test execution
Provides methods that run during test execution.

These methods are attached to behave via monkey patching.
"""

testcase_class = None

def patch_context(self, context):
Expand All @@ -66,7 +69,7 @@ def setup_testclass(self, context):
"""
Adds the test instance to context
"""
context.test = self.testcase_class() # pylint: disable=not-callable
context.test = self.testcase_class()

def setup_fixtures(self, context):
"""
Expand Down
93 changes: 50 additions & 43 deletions behave_django/management/commands/behave.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from __future__ import absolute_import
"""Main entry point, the ``behave`` management command for Django."""

import sys
from argparse import ArgumentTypeError
Expand All @@ -21,18 +21,17 @@ def valid_python_module(path):
module_path, class_name = path.rsplit(':', 1)
module = import_module(module_path)
return getattr(module, class_name)
except (ValueError, ImportError):
except (ValueError, ImportError) as err:
msg = f"Failed to import module '{module_path}'."
raise ArgumentTypeError(msg)
except AttributeError:
raise ArgumentTypeError(msg) from err
except AttributeError as err:
msg = f"No class '{class_name}' found in '{module_path}'."
raise ArgumentTypeError(msg)
raise ArgumentTypeError(msg) from err


def add_command_arguments(parser):
"""
Additional command line arguments for the behave management command
"""
"""Command line arguments for the behave management command."""

parser.add_argument(
'--noinput',
'--no-input',
Expand All @@ -42,12 +41,18 @@ def add_command_arguments(parser):
help='Tells Django to NOT prompt the user for input of any kind.',
)
parser.add_argument(
'--failfast', action='store_const', const=True, dest='failfast',
help=('Tells Django to stop running the '
'test suite after first failed test.'),
'--failfast',
action='store_const',
const=True,
dest='failfast',
help='Tells Django to stop running the test suite after first failed test.',
)
parser.add_argument(
'-r', '--reverse', action='store_const', const=True, dest='reverse',
'-r',
'--reverse',
action='store_const',
const=True,
dest='reverse',
help='Reverses test cases order.',
)
parser.add_argument(
Expand All @@ -57,32 +62,32 @@ def add_command_arguments(parser):
help="Don't create a test database. USE AT YOUR OWN RISK!",
)
parser.add_argument(
'-k', '--keepdb',
'-k',
'--keepdb',
action='store_const',
const=True,
help="Preserves the test DB between runs.",
help='Preserves the test DB between runs.',
)
parser.add_argument(
'-S', '--simple',
'-S',
'--simple',
action='store_true',
default=False,
help="Use simple test runner that supports Django's"
" testing client only (no web browser automation)"
help="Use simple test runner that supports Django's testing client only"
' (no web browser automation)',
)
parser.add_argument(
'--runner',
action='store',
type=valid_python_module,
default='behave_django.runner:BehaviorDrivenTestRunner',
help=('Full Python dotted path to a package, module, Django '
'TestRunner. Defaults to "%(default)s".')
help='Full Python dotted path to a package, module, Django TestRunner.'
' Defaults to "%(default)s".',
)


def add_behave_arguments(parser): # noqa
"""
Additional command line arguments extracted directly from behave
"""
def add_behave_arguments(parser):
"""Additional command line arguments extracted from upstream behave."""

# Option strings that conflict with Django
conflicts = [
Expand All @@ -101,7 +106,7 @@ def add_behave_arguments(parser): # noqa
'paths',
action='store',
nargs='*',
help="Feature directory, file or file location (FILE:LINE)."
help='Feature directory, file or file location (FILE:LINE).',
)

for fixed, keywords in behave_options:
Expand Down Expand Up @@ -133,17 +138,17 @@ class Command(BaseCommand):
help = 'Runs behave tests'

def add_arguments(self, parser):
"""
Add behave's and our command line arguments to the command
"""
parser.usage = "%(prog)s [options] [ [DIR|FILE|FILE:LINE] ]+"
"""Add behave's and our command line arguments to the command."""

parser.usage = '%(prog)s [options] [ [DIR|FILE|FILE:LINE] ]+'
parser.description = """\
Run a number of feature tests with behave."""

add_command_arguments(parser)
add_behave_arguments(parser)

def handle(self, *args, **options):
"""Main entry point when django-behave executes."""

django_runner_class = options['runner']

Expand All @@ -161,25 +166,26 @@ def handle(self, *args, **options):
django_runner_class = SimpleTestRunner

elif options['use_existing_database'] or options['simple']:
self.stderr.write(self.style.WARNING(
'--use-existing-database or --simple has no effect'
' together with --runner'
))
self.stderr.write(
self.style.WARNING(
'--use-existing-database or --simple has no effect'
' together with --runner'
)
)

if options['use_existing_database'] and options['simple']:
self.stderr.write(self.style.WARNING(
'--simple flag has no effect'
' together with --use-existing-database'
))
self.stderr.write(
self.style.WARNING(
'--simple flag has no effect'
' together with --use-existing-database'
)
)

# Configure django environment
passthru_args = ('failfast',
'interactive',
'keepdb',
'reverse')
runner_args = {k: v for
k, v in
options.items() if k in passthru_args and v is not None}
passthru_args = ['failfast', 'interactive', 'keepdb', 'reverse']
runner_args = {
k: v for k, v in options.items() if k in passthru_args and v is not None
}

django_test_runner = django_runner_class(**runner_args)
django_test_runner.setup_test_environment()
Expand Down Expand Up @@ -220,6 +226,7 @@ def get_behave_args(self, argv=sys.argv):


class BehaveArgsHelper(Command):
"""Command line parser for passing arguments down to behave."""

def add_arguments(self, parser):
"""
Expand Down
19 changes: 13 additions & 6 deletions behave_django/pageobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class WrongElementError(RuntimeError):
A different PageObject element was expected by the getter from the
```elements`` dictionary.
"""

def __init__(self, element, expected):
message = f"Expected {expected}, found {element.__class__}"
message = f'Expected {expected}, found {element.__class__}'
super().__init__(message)


Expand All @@ -27,6 +28,7 @@ class PageObject:
:elements:
Dictionary of elements accessible by helper methods
"""

page = None
elements = {}

Expand All @@ -51,11 +53,13 @@ def __eq__(self, other):
Instead of page we compare the request URL path, which is the
resolved value and should always match for equal pages.
"""
return isinstance(other, PageObject) and \
self.elements == other.elements and \
self.document.string == other.document.string and \
self.request == other.request and \
self.response.status_code == other.response.status_code
return (
isinstance(other, PageObject)
and self.elements == other.elements
and self.document.string == other.document.string
and self.request == other.request
and self.response.status_code == other.response.status_code
)

def _get_element_ensure(self, name, ensure):
"""
Expand Down Expand Up @@ -100,7 +104,9 @@ def get_links(self, name):
current_context = self.context
element = self._get_element_ensure(name, Link)
links = self.document.select(element.selector)

for link in links:

def click():
"""Visit a link, load related URL, return a PageObject"""
href = link.get('href')
Expand All @@ -109,6 +115,7 @@ class NewPageObject(PageObject):
page = href

return NewPageObject(current_context)

link.click = click
return links

Expand Down
13 changes: 6 additions & 7 deletions behave_django/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@


class BehaviorDrivenTestRunner(DiscoverRunner, BehaveHooksMixin):
"""
Test runner that uses the BehaviorDrivenTestCase
"""
"""Test runner that uses the BehaviorDrivenTestCase."""

testcase_class = BehaviorDrivenTestCase


class ExistingDatabaseTestRunner(DiscoverRunner, BehaveHooksMixin):
"""
Test runner that uses the ExistingDatabaseTestCase
"""Test runner that uses the ExistingDatabaseTestCase.

This test runner nullifies Django's test database setup methods. Using this
test runner would make your tests run with the default configured database
in settings.py.
"""

testcase_class = ExistingDatabaseTestCase

def setup_databases(self, **kwargs):
Expand All @@ -33,8 +32,8 @@ def teardown_databases(self, old_config, **kwargs):


class SimpleTestRunner(DiscoverRunner, BehaveHooksMixin):
"""
Test runner that uses DjangoSimpleTestCase with atomic
"""Test runner that uses DjangoSimpleTestCase with atomic
transaction management and no support of web browser automation.
"""

testcase_class = DjangoSimpleTestCase
Loading