Skip to content

Commit

Permalink
Fixed watchman tests.
Browse files Browse the repository at this point in the history
Now using wrapt to create "fake" plugins during tests.
  • Loading branch information
christophe-david committed Apr 1, 2022
1 parent 298a918 commit 9a40140
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 86 deletions.
173 changes: 98 additions & 75 deletions src/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@
from unittest.mock import Mock

import pytest
import wrapt

if sys.version_info < (3, 10):
from importlib_metadata import EntryPoint, distribution, Distribution, EntryPoints
if sys.version_info >= (3, 10):
import importlib.metadata as importlib_metadata
else:
from importlib.metadata import EntryPoint, distribution, Distribution, EntryPoints
import importlib_metadata

from fastoad.module_management._plugins import FastoadLoader, MODEL_PLUGIN_ID

Expand Down Expand Up @@ -98,17 +99,17 @@ def with_dummy_plugin_1():
Any previous state of plugins is restored during teardown.
"""
_setup()
dummy_dist_1 = Mock(Distribution)
dummy_dist_1 = Mock(importlib_metadata.Distribution)
dummy_dist_1.name = "dummy-dist-1"
entry_points = [
EntryPoint(
new_entry_points = [
importlib_metadata.EntryPoint(
name="test_plugin_1",
value="tests.dummy_plugins.dist_1.dummy_plugin_1",
group=MODEL_PLUGIN_ID,
)
]
entry_points[0].dist = dummy_dist_1
_update_entry_map(entry_points)
new_entry_points[0].dist = dummy_dist_1
_update_entry_map(new_entry_points)
yield
_teardown()

Expand All @@ -122,17 +123,17 @@ def with_dummy_plugin_2():
Any previous state of plugins is restored during teardown.
"""
_setup()
dummy_dist_2 = Mock(Distribution)
dummy_dist_2 = Mock(importlib_metadata.Distribution)
dummy_dist_2.name = "dummy-dist-2"
entry_points = [
EntryPoint(
new_entry_points = [
importlib_metadata.EntryPoint(
name="test_plugin_2",
value="tests.dummy_plugins.dist_2.dummy_plugin_2",
group=MODEL_PLUGIN_ID,
)
]
entry_points[0].dist = dummy_dist_2
_update_entry_map(entry_points)
new_entry_points[0].dist = dummy_dist_2
_update_entry_map(new_entry_points)
yield
_teardown()

Expand All @@ -146,17 +147,17 @@ def with_dummy_plugin_4():
Any previous state of plugins is restored during teardown.
"""
_setup()
dummy_dist_1 = Mock(Distribution)
dummy_dist_1 = Mock(importlib_metadata.Distribution)
dummy_dist_1.name = "dummy-dist-1"
entry_points = [
EntryPoint(
new_entry_points = [
importlib_metadata.EntryPoint(
name="test_plugin_4",
value="tests.dummy_plugins.dist_1.dummy_plugin_4",
group=MODEL_PLUGIN_ID,
)
]
entry_points[0].dist = dummy_dist_1
_update_entry_map(entry_points)
new_entry_points[0].dist = dummy_dist_1
_update_entry_map(new_entry_points)
yield
_teardown()

Expand All @@ -170,24 +171,24 @@ def with_dummy_plugin_distribution_1():
Any previous state of plugins is restored during teardown.
"""
_setup()
dummy_dist_1 = Mock(Distribution)
dummy_dist_1 = Mock(importlib_metadata.Distribution)
dummy_dist_1.name = "dummy-dist-1"
entry_points = [
EntryPoint(
new_entry_points = [
importlib_metadata.EntryPoint(
name="test_plugin_1",
value="tests.dummy_plugins.dist_1.dummy_plugin_1",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_4",
value="tests.dummy_plugins.dist_1.dummy_plugin_4",
group=MODEL_PLUGIN_ID,
),
]

entry_points[0].dist = entry_points[1].dist = dummy_dist_1
new_entry_points[0].dist = new_entry_points[1].dist = dummy_dist_1

_update_entry_map(entry_points)
_update_entry_map(new_entry_points)
yield
_teardown()

Expand All @@ -201,31 +202,31 @@ def with_dummy_plugin_distribution_1_and_3():
Any previous state of plugins is restored during teardown.
"""
_setup()
dummy_dist_1 = Mock(Distribution)
dummy_dist_1 = Mock(importlib_metadata.Distribution)
dummy_dist_1.name = "dummy-dist-1"
dummy_dist_3 = Mock(Distribution)
dummy_dist_3 = Mock(importlib_metadata.Distribution)
dummy_dist_3.name = "dummy-dist-3"
entry_points = [
EntryPoint(
new_entry_points = [
importlib_metadata.EntryPoint(
name="test_plugin_1",
value="tests.dummy_plugins.dist_1.dummy_plugin_1",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_4",
value="tests.dummy_plugins.dist_1.dummy_plugin_4",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_5",
value="tests.dummy_plugins.dist_3.dummy_plugin_5",
group=MODEL_PLUGIN_ID,
),
]
entry_points[0].dist = entry_points[1].dist = dummy_dist_1
entry_points[2].dist = dummy_dist_3
new_entry_points[0].dist = new_entry_points[1].dist = dummy_dist_1
new_entry_points[2].dist = dummy_dist_3

_update_entry_map(entry_points)
_update_entry_map(new_entry_points)
yield
_teardown()

Expand All @@ -244,89 +245,111 @@ def with_dummy_plugins():
Any previous state of plugins is restored during teardown.
"""
_setup()
dummy_dist_1 = Mock(Distribution)
dummy_dist_1 = Mock(importlib_metadata.Distribution)
dummy_dist_1.name = "dummy-dist-1"
dummy_dist_2 = Mock(Distribution)
dummy_dist_2 = Mock(importlib_metadata.Distribution)
dummy_dist_2.name = "dummy-dist-2"
dummy_dist_3 = Mock(Distribution)
dummy_dist_3 = Mock(importlib_metadata.Distribution)
dummy_dist_3.name = "dummy-dist-3"
entry_points = [
EntryPoint(
new_entry_points = [
importlib_metadata.EntryPoint(
name="test_plugin_1",
value="tests.dummy_plugins.dist_1.dummy_plugin_1",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_4",
value="tests.dummy_plugins.dist_1.dummy_plugin_4",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_2",
value="tests.dummy_plugins.dist_2.dummy_plugin_2",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_3",
value="tests.dummy_plugins.dist_2.dummy_plugin_3",
group=MODEL_PLUGIN_ID,
),
EntryPoint(
importlib_metadata.EntryPoint(
name="test_plugin_5",
value="tests.dummy_plugins.dist_3.dummy_plugin_5",
group=MODEL_PLUGIN_ID,
),
]
entry_points[0].dist = entry_points[1].dist = dummy_dist_1
entry_points[2].dist = entry_points[3].dist = dummy_dist_2
entry_points[4].dist = dummy_dist_3
new_entry_points[0].dist = new_entry_points[1].dist = dummy_dist_1
new_entry_points[2].dist = new_entry_points[3].dist = dummy_dist_2
new_entry_points[4].dist = dummy_dist_3

_update_entry_map(entry_points)
_update_entry_map(new_entry_points)
yield
_teardown()


ORIGINAL_ENTRY_POINTS_PROPERTY = Distribution.entry_points
try:
ORIGINAL_ENTRY_POINT_SETATTR = Distribution.__setattr__
except AttributeError:
ORIGINAL_ENTRY_POINT_SETATTR = None


def _update_entry_map(new_plugin_entry_points: List[EntryPoint]):
def _update_entry_map(new_plugin_entry_points: List[importlib_metadata.EntryPoint]):
"""
Modified plugin entry_points of FAST-OAD distribution.
This is done by replacing the entry_points property of Distribution class
:param new_plugin_entry_points:
"""
dist: Distribution = distribution("FAST-OAD-core")

entry_points = EntryPoints(
[ep for ep in dist.entry_points if ep.group != MODEL_PLUGIN_ID] + new_plugin_entry_points
)

# Distribution.entry_points gets info directly from entry_points.txt.
# Therefore, the only way to modify the output of Distribution.entry_points
# is to replace the property.
setattr(Distribution, "entry_points", property(lambda self: entry_points))

# Ensure next instantiation of FastoadLoader will trigger reloading plugins
BypassEntryPointReading.entry_points = new_plugin_entry_points
BypassEntryPointReading.active = True
FastoadLoader._loaded = False


def _setup():
# In last versions of importlib-metadata, EntryPoint overloads __setattr__ to
# prevent any attribute modification, but it does not suit our needs.
try:
delattr(EntryPoint, "__setattr__")
except AttributeError:
pass
MakeEntryPointMutable.active = True


def _teardown():
if ORIGINAL_ENTRY_POINT_SETATTR:
setattr(EntryPoint, "__setattr__", ORIGINAL_ENTRY_POINT_SETATTR)
setattr(Distribution, "entry_points", ORIGINAL_ENTRY_POINTS_PROPERTY)
MakeEntryPointMutable.active = False
BypassEntryPointReading.active = False
FastoadLoader._loaded = False


# Monkey-patching using wrapt module ###########################################


def _BypassEntryPointReading_enabled():
return BypassEntryPointReading.active


class BypassEntryPointReading:
active = False
entry_points = []

@wrapt.decorator(enabled=_BypassEntryPointReading_enabled)
def __call__(self, wrapped, instance, args, kwargs):
if kwargs.get("group") == MODEL_PLUGIN_ID:
return self.entry_points
else:
return wrapped(*args, **kwargs)


importlib_metadata.entry_points = BypassEntryPointReading()(importlib_metadata.entry_points)


def _MakeEntryPointMutable_enabled():
return MakeEntryPointMutable.active


class MakeEntryPointMutable:
active = True

@classmethod
def _enabled(cls):
return cls.active

@wrapt.decorator(enabled=_MakeEntryPointMutable_enabled)
def __call__(self, wrapped, instance, args, kwargs):
try:
delattr(wrapped, "__setattr__")
except AttributeError:
pass
return wrapped(*args, **kwargs)


importlib_metadata.EntryPoint = MakeEntryPointMutable()(importlib_metadata.EntryPoint)
12 changes: 6 additions & 6 deletions src/fastoad/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@

import sys

if sys.version_info < (3, 10):
from importlib_metadata import distribution, PackageNotFoundError
if sys.version_info >= (3, 10):
import importlib.metadata as importlib_metadata
else:
from importlib.metadata import distribution, PackageNotFoundError
import importlib_metadata

try:
# Change here if project is renamed and does not equal the package name
dist_name = "FAST-OAD-core"
__version__ = distribution(dist_name).version
except PackageNotFoundError:
__version__ = importlib_metadata.distribution(dist_name).version
except importlib_metadata.PackageNotFoundError:
__version__ = "unknown"
finally:
del distribution
del importlib_metadata
16 changes: 11 additions & 5 deletions src/fastoad/module_management/_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
)
from .._utils.resource_management.contents import PackageReader

if sys.version_info < (3, 10):
from importlib_metadata import entry_points, EntryPoint
if sys.version_info >= (3, 10):
import importlib.metadata as importlib_metadata
else:
from importlib.metadata import entry_points, EntryPoint
import importlib_metadata

_LOGGER = logging.getLogger(__name__) # Logger for this module

Expand Down Expand Up @@ -114,7 +114,7 @@ class DistributionPluginDefinition(dict):

dist_name: str = None

def read_entry_point(self, entry_point: EntryPoint, group: str):
def read_entry_point(self, entry_point: importlib_metadata.EntryPoint, group: str):
"""
Adds plugin definition from provided entry point to
Expand Down Expand Up @@ -337,8 +337,14 @@ def read_entry_points(cls):
"""
Reads definitions of declared plugins.
"""
# Dev note: in src/conftest.py, wrapt is used to monkey patch
# importlib_metadata.entry_points when unit testing plugin-related feature.
# This will not work if `from importlib_metadata import entry_points`
# is done at beginning of the file, because then, we will use a reference
# to original `entry_points`, before it is patched by conftest.py

for group in [OLD_MODEL_PLUGIN_ID, MODEL_PLUGIN_ID]:
for entry_point in entry_points(group=group):
for entry_point in importlib_metadata.entry_points(group=group):
if entry_point.dist.name not in cls._dist_plugin_definitions:
cls._dist_plugin_definitions[
entry_point.dist.name
Expand Down

0 comments on commit 9a40140

Please sign in to comment.