Skip to content

Commit

Permalink
CLEANUP: behave.matchers
Browse files Browse the repository at this point in the history
* Provide StepMatcherFactory to better keep track of things
* Matcher classes: Provide register_type(), ... to better keep track
  of Matcher specific type-converters.

OTHERWISE:

* ADDED: behave.api.step_matchers -- Provides public API for step writers
* behave._stepimport: Added SimpleStepContainer to simplify reuse
  HINT: Moved here from tests.api.testing_support module.
  • Loading branch information
jenisys committed Jun 8, 2023
1 parent 0e7eb26 commit ca371cd
Show file tree
Hide file tree
Showing 18 changed files with 1,325 additions and 565 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ reports/
tools/virtualenvs
.cache/
.direnv/
.fleet/
.idea/
.pytest_cache/
.tox/
Expand Down
757 changes: 519 additions & 238 deletions .pylintrc

Large diffs are not rendered by default.

15 changes: 11 additions & 4 deletions behave/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,22 @@
"""

from __future__ import absolute_import
from behave.step_registry import given, when, then, step, Given, When, Then, Step # pylint: disable=no-name-in-module
from behave.matchers import use_step_matcher, step_matcher, register_type
# pylint: disable=no-name-in-module
from behave.step_registry import given, when, then, step, Given, When, Then, Step
# pylint: enable=no-name-in-module
from behave.api.step_matchers import (
register_type,
use_default_step_matcher, use_step_matcher,
step_matcher
)
from behave.fixture import fixture, use_fixture
from behave.version import VERSION as __version__
from behave.version import VERSION as __version__ # noqa: F401

# pylint: disable=undefined-all-variable
__all__ = [
"given", "when", "then", "step", "use_step_matcher", "register_type",
"given", "when", "then", "step",
"Given", "When", "Then", "Step",
"use_default_step_matcher", "use_step_matcher", "register_type",
"fixture", "use_fixture",
# -- DEPRECATING:
"step_matcher"
Expand Down
96 changes: 52 additions & 44 deletions behave/_stepimport.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: UTF-8 -*-
# pylint: disable=useless-object-inheritance
# pylint: disable=super-with-arguments
"""
This module provides low-level helper functionality during step imports.
Expand All @@ -15,21 +17,35 @@
from types import ModuleType
import os.path
import sys
from behave import step_registry as _step_registry
# from behave import matchers as _matchers
import six

from behave import step_registry as _step_registry
from behave.matchers import StepMatcherFactory
from behave.step_registry import StepRegistry


# -----------------------------------------------------------------------------
# UTILITY FUNCTIONS:
# -----------------------------------------------------------------------------
def setup_api_with_step_decorators(module, step_registry):
_step_registry.setup_step_decorators(module, step_registry)

def setup_api_with_matcher_functions(module, matcher_factory):
module.use_step_matcher = matcher_factory.use_step_matcher
module.step_matcher = matcher_factory.use_step_matcher
module.register_type = matcher_factory.register_type
def setup_api_with_matcher_functions(module, step_matcher_factory):
# -- PUBLIC API: Same as behave.api.step_matchers
module.use_default_step_matcher = step_matcher_factory.use_default_step_matcher
module.use_step_matcher = step_matcher_factory.use_step_matcher
module.step_matcher = step_matcher_factory.use_step_matcher
module.register_type = step_matcher_factory.register_type


class SimpleStepContainer(object):
def __init__(self, step_registry=None):
if step_registry is None:
step_registry = StepRegistry()
self.step_matcher_factory = StepMatcherFactory()
self.step_registry = step_registry
self.step_registry.step_matcher_factory = self.step_matcher_factory


# -----------------------------------------------------------------------------
# FAKE MODULE CLASSES: For step imports
Expand Down Expand Up @@ -60,14 +76,20 @@ def __init__(self, step_registry):


class StepMatchersModule(FakeModule):
__all__ = ["use_step_matcher", "register_type", "step_matcher"]
__all__ = [
"use_default_step_matcher",
"use_step_matcher",
"step_matcher", # -- DEPRECATING
"register_type"
]

def __init__(self, matcher_factory):
def __init__(self, step_matcher_factory):
super(StepMatchersModule, self).__init__("behave.matchers")
self.matcher_factory = matcher_factory
setup_api_with_matcher_functions(self, matcher_factory)
self.use_default_step_matcher = matcher_factory.use_default_step_matcher
self.get_matcher = matcher_factory.make_matcher
self.step_matcher_factory = step_matcher_factory
setup_api_with_matcher_functions(self, step_matcher_factory)
self.make_matcher = step_matcher_factory.make_matcher
# -- DEPRECATED-FUNCTION-COMPATIBILITY
# self.get_matcher = self.make_matcher
# self.matcher_mapping = matcher_mapping or _matchers.matcher_mapping.copy()
# self.current_matcher = current_matcher or _matchers.current_matcher

Expand All @@ -78,36 +100,19 @@ def __init__(self, matcher_factory):
self.__name__ = "behave.matchers"
# self.__path__ = [os.path.abspath(here)]

# def use_step_matcher(self, name):
# self.matcher_factory.use_step_matcher(name)
# # self.current_matcher = self.matcher_mapping[name]
#
# def use_default_step_matcher(self, name=None):
# self.matcher_factory.use_default_step_matcher(name=None)
#
# def get_matcher(self, func, pattern):
# # return self.current_matcher
# return self.matcher_factory.make_matcher(func, pattern)
#
# def register_type(self, **kwargs):
# # _matchers.register_type(**kwargs)
# self.matcher_factory.register_type(**kwargs)
#
# step_matcher = use_step_matcher


class BehaveModule(FakeModule):
__all__ = StepRegistryModule.__all__ + StepMatchersModule.__all__

def __init__(self, step_registry, matcher_factory=None):
if matcher_factory is None:
matcher_factory = step_registry.step_matcher_factory
assert matcher_factory is not None
def __init__(self, step_registry, step_matcher_factory=None):
if step_matcher_factory is None:
step_matcher_factory = step_registry.step_step_matcher_factory
assert step_matcher_factory is not None
super(BehaveModule, self).__init__("behave")
setup_api_with_step_decorators(self, step_registry)
setup_api_with_matcher_functions(self, matcher_factory)
self.use_default_step_matcher = matcher_factory.use_default_step_matcher
assert step_registry.matcher_factory == matcher_factory
setup_api_with_matcher_functions(self, step_matcher_factory)
self.use_default_step_matcher = step_matcher_factory.use_default_step_matcher
assert step_registry.step_matcher_factory == step_matcher_factory

# -- INJECT PYTHON PACKAGE META-DATA:
# REQUIRED-FOR: Non-fake submodule imports (__path__).
Expand All @@ -122,13 +127,13 @@ class StepImportModuleContext(object):

def __init__(self, step_container):
self.step_registry = step_container.step_registry
self.matcher_factory = step_container.matcher_factory
assert self.step_registry.matcher_factory == self.matcher_factory
self.step_registry.matcher_factory = self.matcher_factory
self.step_matcher_factory = step_container.step_matcher_factory
assert self.step_registry.step_matcher_factory == self.step_matcher_factory
self.step_registry.step_matcher_factory = self.step_matcher_factory

step_registry_module = StepRegistryModule(self.step_registry)
step_matchers_module = StepMatchersModule(self.matcher_factory)
behave_module = BehaveModule(self.step_registry, self.matcher_factory)
step_matchers_module = StepMatchersModule(self.step_matcher_factory)
behave_module = BehaveModule(self.step_registry, self.step_matcher_factory)
self.modules = {
"behave": behave_module,
"behave.matchers": step_matchers_module,
Expand All @@ -137,14 +142,16 @@ def __init__(self, step_container):
# self.default_matcher = self.step_matchers_module.current_matcher

def reset_current_matcher(self):
self.matcher_factory.use_default_step_matcher()
self.step_matcher_factory.use_default_step_matcher()


_step_import_lock = Lock()
unknown = object()

@contextmanager
def use_step_import_modules(step_container):
"""Redirect any step/type registration to the runner's step-context object
"""
Redirect any step/type registration to the runner's step-context object
during step imports by using fake modules (instead of using module-globals).
This allows that multiple runners can be used without polluting the
Expand All @@ -161,7 +168,8 @@ def load_step_definitions(self, ...):
...
import_context.reset_current_matcher()
:param step_container: Step context object with step_registry, matcher_factory.
:param step_container:
Step context object with step_registry, step_matcher_factory.
"""
orig_modules = {}
import_context = StepImportModuleContext(step_container)
Expand Down
32 changes: 32 additions & 0 deletions behave/api/step_matchers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: UTF-8 -*-
"""
Official API for step writers that want to use step-matchers.
"""

from __future__ import absolute_import, print_function
import warnings
from behave import matchers as _step_matchers


def register_type(**kwargs):
_step_matchers.register_type(**kwargs)


def use_default_step_matcher(name=None):
return _step_matchers.use_default_step_matcher(name=name)

def use_step_matcher(name):
return _step_matchers.use_step_matcher(name)

def step_matcher(name):
"""DEPRECATED, use :func:`use_step_matcher()` instead."""
# -- BACKWARD-COMPATIBLE NAME: Mark as deprecated.
warnings.warn("deprecated: Use 'use_step_matcher()' instead",
DeprecationWarning, stacklevel=2)
return use_step_matcher(name)


# -- REUSE: API function descriptions (aka: docstrings).
register_type.__doc__ = _step_matchers.register_type.__doc__
use_step_matcher.__doc__ = _step_matchers.use_step_matcher.__doc__
use_default_step_matcher.__doc__ = _step_matchers.use_default_step_matcher.__doc__
24 changes: 20 additions & 4 deletions behave/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,30 @@
.. versionadded:: 1.2.7
"""

from __future__ import absolute_import
from __future__ import absolute_import, print_function
# -- USE MODERN EXCEPTION CLASSES:
# COMPATIBILITY: Emulated if not supported yet by Python version.
from behave.compat.exceptions import FileNotFoundError, ModuleNotFoundError

from behave.compat.exceptions import (
FileNotFoundError, ModuleNotFoundError # noqa: F401
)

# ---------------------------------------------------------------------------
# EXCEPTION/ERROR CLASSES:
# ---------------------------------------------------------------------------
class ConstraintError(RuntimeError):
"""Used if a constraint/precondition is not fulfilled at runtime.
"""
Used if a constraint/precondition is not fulfilled at runtime.
.. versionadded:: 1.2.7
"""

class ResourceExistsError(ConstraintError):
"""
Used if you try to register a resource and another exists already
with the same name.
.. versionadded:: 1.2.7
"""

class ConfigError(Exception):
"""Used if the configuration is (partially) invalid."""
Expand Down Expand Up @@ -62,3 +71,10 @@ class InvalidClassError(TypeError):
* not a class
* not subclass of a required class
"""

class NotSupportedWarning(Warning):
"""
Used if a certain functionality is not supported.
.. versionadded:: 1.2.7
"""

0 comments on commit ca371cd

Please sign in to comment.