Skip to content

Commit da4003d

Browse files
authored
Merge pull request #22 from mutating/develop
0.0.20
2 parents 1310d11 + c011b13 commit da4003d

File tree

6 files changed

+62
-21
lines changed

6 files changed

+62
-21
lines changed

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@
33

44
[![Downloads](https://static.pepy.tech/badge/locklib/month)](https://pepy.tech/project/locklib)
55
[![Downloads](https://static.pepy.tech/badge/locklib)](https://pepy.tech/project/locklib)
6-
[![Coverage Status](https://coveralls.io/repos/github/pomponchik/locklib/badge.svg?branch=main)](https://coveralls.io/github/pomponchik/locklib?branch=main)
7-
[![Lines of code](https://sloc.xyz/github/pomponchik/locklib/?category=code?)](https://github.com/boyter/scc/)
8-
[![Hits-of-Code](https://hitsofcode.com/github/pomponchik/locklib?branch=main)](https://hitsofcode.com/github/pomponchik/locklib/view?branch=main)
9-
[![Test-Package](https://github.com/pomponchik/locklib/actions/workflows/tests_and_coverage.yml/badge.svg)](https://github.com/pomponchik/locklib/actions/workflows/tests_and_coverage.yml)
6+
[![Coverage Status](https://coveralls.io/repos/github/mutating/locklib/badge.svg?branch=main)](https://coveralls.io/github/mutating/locklib?branch=main)
7+
[![Lines of code](https://sloc.xyz/github/mutating/locklib/?category=code?)](https://github.com/boyter/scc/)
8+
[![Hits-of-Code](https://hitsofcode.com/github/mutating/locklib?branch=main)](https://hitsofcode.com/github/mutating/locklib/view?branch=main)
9+
[![Test-Package](https://github.com/mutating/locklib/actions/workflows/tests_and_coverage.yml/badge.svg)](https://github.com/mutating/locklib/actions/workflows/tests_and_coverage.yml)
1010
[![Python versions](https://img.shields.io/pypi/pyversions/locklib.svg)](https://pypi.python.org/pypi/locklib)
1111
[![PyPI version](https://badge.fury.io/py/locklib.svg)](https://badge.fury.io/py/locklib)
1212
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
1313
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
14-
[![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/pomponchik/locklib)
14+
[![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/mutating/locklib)
1515

1616
</details>
1717

1818
<p align="center">
1919

20-
![logo](https://raw.githubusercontent.com/pomponchik/locklib/develop/docs/assets/logo_7.svg)
20+
![logo](https://raw.githubusercontent.com/mutating/locklib/develop/docs/assets/logo_7.svg)
2121

2222
</p>
2323

@@ -43,7 +43,7 @@ pip install locklib
4343
... or directly from git:
4444

4545
```bash
46-
pip install git+https://github.com/pomponchik/locklib.git
46+
pip install git+https://github.com/mutating/locklib.git
4747
```
4848

4949
You can also quickly try out this and other packages without having to install using [instld](https://github.com/pomponchik/instld).
@@ -219,4 +219,6 @@ If the `notify` method was called with the same parameter only when the lock act
219219

220220
How does it work? A modified [algorithm for determining the correct parenthesis sequence](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%81%D0%BA%D0%BE%D0%B1%D0%BE%D1%87%D0%BD%D0%B0%D1%8F_%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C) is used here. For each thread for which any events were registered (taking the mutex, releasing the mutex, and also calling the `notify` method), the check takes place separately, that is, we determine that it was the same thread that held the mutex when `notify` was called, and not some other one.
221221

222-
> ⚠️ The thread id is used to identify the streams. This id may be reused if the current thread ends, which in some cases may lead to incorrect identification of lock coverage for operations that were not actually covered by the lock. Make sure that this cannot happen during your test.
222+
> ⚠️ The thread id is used to identify the threads. This id may be reused if the current thread ends, which in some cases may lead to incorrect identification of lock coverage for operations that were not actually covered by the lock. Make sure that this cannot happen during your test.
223+
224+
If no event with the specified identifier was recorded in any of the threads, the `ThereWasNoSuchEventError` exception will be raised by default. If you want to disable this so that the method simply returns `False` in such situations, pass the additional argument `raise_exception=False` to `was_event_locked`.

locklib/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
from locklib.errors import (
33
StrangeEventOrderError as StrangeEventOrderError,
44
)
5+
from locklib.errors import (
6+
ThereWasNoSuchEventError as ThereWasNoSuchEventError,
7+
)
58
from locklib.locks.smart_lock.lock import SmartLock as SmartLock
69
from locklib.locks.tracer.tracer import (
710
LockTraceWrapper as LockTraceWrapper,

locklib/errors.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
class DeadLockError(Exception):
2-
pass
2+
...
33

44
class StrangeEventOrderError(Exception):
5-
pass
5+
...
6+
7+
class ThereWasNoSuchEventError(Exception):
8+
...

locklib/locks/tracer/tracer.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from types import TracebackType
44
from typing import Dict, List, Optional, Type
55

6-
from locklib.errors import StrangeEventOrderError
6+
from locklib.errors import StrangeEventOrderError, ThereWasNoSuchEventError
77
from locklib.locks.tracer.events import TracerEvent, TracerEventType
88
from locklib.protocols.lock import LockProtocol
99

@@ -46,9 +46,11 @@ def notify(self, identifier: str) -> None:
4646
),
4747
)
4848

49-
def was_event_locked(self, identifier: str) -> bool:
49+
def was_event_locked(self, identifier: str, raise_exception: bool = True) -> bool:
5050
stacks: Dict[int, List[TracerEvent]] = defaultdict(list)
5151

52+
there_was_action_with_this_identifier = False
53+
5254
for event in self.trace:
5355
stack = stacks[event.thread_id]
5456

@@ -57,11 +59,18 @@ def was_event_locked(self, identifier: str) -> bool:
5759

5860
elif event.type == TracerEventType.RELEASE:
5961
if not stack:
60-
raise StrangeEventOrderError('Release event without corresponding acquire event.')
62+
if raise_exception:
63+
raise StrangeEventOrderError('Release event without corresponding acquire event.')
64+
return False
6165
stack.pop()
6266

6367
elif event.type == TracerEventType.ACTION:
64-
if event.identifier == identifier and not stack:
65-
return False
68+
if event.identifier == identifier:
69+
there_was_action_with_this_identifier = True
70+
if not stack:
71+
return False
72+
73+
if (not there_was_action_with_this_identifier) and raise_exception:
74+
raise ThereWasNoSuchEventError(f'No events with identifier "{identifier}" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')
6675

67-
return True
76+
return there_was_action_with_this_identifier

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
44

55
[project]
66
name = 'locklib'
7-
version = '0.0.19'
7+
version = '0.0.20'
88
authors = [
99
{ name='Evgeniy Blinov', email='zheni-b@yandex.ru' },
1010
]

tests/units/locks/tracer/test_tracer.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from typing import List, Union
44

55
import pytest
6+
from full_match import match
67

7-
from locklib import LockTraceWrapper, StrangeEventOrderError
8+
from locklib import LockTraceWrapper, StrangeEventOrderError, ThereWasNoSuchEventError
89
from locklib.locks.tracer.events import TracerEvent, TracerEventType
910

1011

@@ -72,11 +73,22 @@ def release(self):
7273
with pytest.raises(StrangeEventOrderError):
7374
wrapper.was_event_locked('kek')
7475

76+
with pytest.raises(StrangeEventOrderError):
77+
wrapper.was_event_locked('kek', raise_exception=True)
78+
79+
assert not wrapper.was_event_locked('kek', raise_exception=False)
80+
7581

7682
def test_event_is_locked_if_there_was_no_events():
7783
wrapper = LockTraceWrapper(Lock())
7884

79-
assert wrapper.was_event_locked('kek')
85+
assert not wrapper.was_event_locked('kek', raise_exception=False)
86+
87+
with pytest.raises(ThereWasNoSuchEventError, match=match('No events with identifier "kek" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')):
88+
wrapper.was_event_locked('kek')
89+
90+
with pytest.raises(ThereWasNoSuchEventError, match=match('No events with identifier "kek" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')):
91+
wrapper.was_event_locked('kek', raise_exception=True)
8092

8193

8294
def test_event_is_locked_if_there_are_only_opening_and_slosing_events():
@@ -85,7 +97,13 @@ def test_event_is_locked_if_there_are_only_opening_and_slosing_events():
8597
with wrapper:
8698
pass
8799

88-
assert wrapper.was_event_locked('kek')
100+
assert not wrapper.was_event_locked('kek', raise_exception=False)
101+
102+
with pytest.raises(ThereWasNoSuchEventError, match=match('No events with identifier "kek" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')):
103+
wrapper.was_event_locked('kek')
104+
105+
with pytest.raises(ThereWasNoSuchEventError, match=match('No events with identifier "kek" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')):
106+
wrapper.was_event_locked('kek', raise_exception=True)
89107

90108

91109
def test_simple_case_of_locked_event():
@@ -208,4 +226,10 @@ def test_unknown_event_type():
208226

209227
wrapper.trace.append(TracerEvent('unknown', 1))
210228

211-
assert wrapper.was_event_locked('lol')
229+
assert not wrapper.was_event_locked('lol', raise_exception=False)
230+
231+
with pytest.raises(ThereWasNoSuchEventError, match=match('No events with identifier "lol" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')):
232+
wrapper.was_event_locked('lol')
233+
234+
with pytest.raises(ThereWasNoSuchEventError, match=match('No events with identifier "lol" occurred in any of the threads, so the question "was it thread-safe" is meaningless.')):
235+
wrapper.was_event_locked('lol', raise_exception=True)

0 commit comments

Comments
 (0)