Skip to content
Closed
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
5 changes: 2 additions & 3 deletions radish/customtyperegistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@

# Keep for backwards compat:
from parse_type import TypeBuilder # noqa: F401
from singleton import singleton

from .exceptions import RadishError
from .utils import Singleton


@singleton()
class CustomTypeRegistry:
class CustomTypeRegistry(metaclass=Singleton):
"""
Registry for all custom argument expressions
"""
Expand Down
5 changes: 2 additions & 3 deletions radish/extensionregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@
Provide plugin interface for radish extensions
"""

from singleton import singleton
from .utils import Singleton


@singleton()
class ExtensionRegistry:
class ExtensionRegistry(metaclass=Singleton):
"""
Registers all extensions
"""
Expand Down
5 changes: 2 additions & 3 deletions radish/hookregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
"""

import tagexpressions
from singleton import singleton

from . import utils
from .exceptions import HookError
from .utils import Singleton


@singleton()
class HookRegistry:
class HookRegistry(metaclass=Singleton):
"""
Represents an object with all registered hooks
"""
Expand Down
6 changes: 2 additions & 4 deletions radish/stepregistry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
import inspect
import re

from singleton import singleton

from .exceptions import RadishError, SameStepError, StepRegexError
from .utils import Singleton


@singleton()
class StepRegistry:
class StepRegistry(metaclass=Singleton):
"""
Represents the step registry
"""
Expand Down
16 changes: 16 additions & 0 deletions radish/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import traceback
import warnings
from datetime import datetime, timedelta, timezone
from threading import Lock


class Failure:
Expand Down Expand Up @@ -230,3 +231,18 @@ def split_unescape(s, delim, escape="\\", unescape=True):
current.append(ch)
ret.append("".join(current))
return ret


class Singleton(type):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

location is fine a bit more comments how that works and how to use it here would be nice

"""
Metaclass for singleton classes
"""

_instances = {}
_lock = Lock()

def __call__(cls, *args, **kwargs):
with cls._lock:
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pysingleton==0.2.1
colorful==0.5.8
docopt==0.6.2
ipython==7.34.0
Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ def get_meta(name):
# mandatory requirements for the radish base features
requirements = [
"docopt",
"pysingleton",
"colorful>=0.3.11",
"tag-expressions>=2.0.0",
"parse_type>0.4.0",
Expand Down
42 changes: 42 additions & 0 deletions tests/unit/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

from datetime import datetime, timezone
from threading import Lock, Thread

import pytest
from freezegun import freeze_time
Expand Down Expand Up @@ -67,3 +68,44 @@ def test_make_unique_obj_list():
value_list.sort()

assert value_list == ["1", "2"]


def test_singleton_behavior():
"""Test that Singleton metaclass enforces singleton behavior"""

class MySingletonClass(metaclass=utils.Singleton):
def __init__(self):
self.value = 42

instance1 = MySingletonClass()
instance1.value = 100
instance2 = MySingletonClass()

assert instance1 is instance2
assert instance1.value == instance2.value


def test_singleton_thread_safety():
"""Test that Singleton metaclass is thread-safe"""

class MyThreadSafeSingleton(metaclass=utils.Singleton):
def __init__(self):
self.value = 0

instances = []
lock = Lock()

def create_instance():
instance = MyThreadSafeSingleton()
with lock: # Ensure thread-safe appending
instances.append(instance)

threads = [Thread(target=create_instance) for _ in range(10)]
for thread in threads:
thread.start()
for thread in threads:
thread.join()

first_instance = instances[0]
for instance in instances:
assert instance is first_instance
Loading