Skip to content

Commit feae2af

Browse files
authored
Merge pull request #20 from pomponchik/develop
0.0.18
2 parents 10d1724 + 40e3aa7 commit feae2af

File tree

22 files changed

+800
-119
lines changed

22 files changed

+800
-119
lines changed

.github/workflows/lint.yml

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,55 @@
11
name: Lint
22

3-
on:
4-
push
3+
on: push
54

65
jobs:
76
build:
8-
97
runs-on: ${{ matrix.os }}
108
strategy:
119
matrix:
1210
os: [ubuntu-latest]
13-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
11+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
1412

1513
steps:
16-
- uses: actions/checkout@v2
14+
- uses: actions/checkout@v4
1715

18-
- name: Set up Python ${{ matrix.python-version }}
19-
uses: actions/setup-python@v3
20-
with:
16+
- name: Set up Python ${{ matrix.python-version }}
17+
uses: actions/setup-python@v5
18+
with:
2119
python-version: ${{ matrix.python-version }}
2220

23-
- name: Install dependencies
24-
shell: bash
25-
run: pip install -r requirements_dev.txt
26-
27-
- name: Run mypy
28-
shell: bash
29-
run: mypy locklib --strict
30-
31-
- name: Run mypy for tests
32-
shell: bash
33-
run: mypy tests
34-
35-
- name: Run mypy in strict mode for protocols tests
36-
shell: bash
37-
run: mypy tests/units/protocols/ --strict
38-
39-
- name: Run ruff
40-
shell: bash
41-
run: ruff check locklib
42-
43-
- name: Run ruff for tests
44-
shell: bash
45-
run: ruff check tests
21+
- name: Cache pip dependencies
22+
uses: actions/cache@v4
23+
with:
24+
path: ~/.cache/pip
25+
key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
26+
restore-keys: |
27+
${{ runner.os }}-pip-${{ github.workflow }}-
28+
29+
- name: Install dependencies
30+
shell: bash
31+
run: pip install -r requirements_dev.txt
32+
33+
- name: Install the library
34+
shell: bash
35+
run: pip install .
36+
37+
- name: Run ruff
38+
shell: bash
39+
run: ruff check locklib
40+
41+
- name: Run ruff for tests
42+
shell: bash
43+
run: ruff check tests
44+
45+
- name: Run mypy
46+
shell: bash
47+
run: mypy locklib --strict
48+
49+
- name: Run mypy for tests
50+
shell: bash
51+
run: mypy tests
52+
53+
- name: Run mypy in strict mode for protocols tests
54+
shell: bash
55+
run: mypy tests/units/protocols/ --strict

.github/workflows/publish.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Publish the package
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
pypi-publish:
10+
name: upload release to PyPI
11+
runs-on: ubuntu-latest
12+
# Specifying a GitHub environment is optional, but strongly encouraged
13+
environment: release
14+
permissions:
15+
# IMPORTANT: this permission is mandatory for trusted publishing
16+
id-token: write
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
25+
- name: Install dependencies
26+
shell: bash
27+
run: pip install -r requirements_dev.txt
28+
29+
- name: Build the project
30+
shell: bash
31+
run: python -m build .
32+
33+
- name: Publish package distributions to PyPI
34+
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/release.yml

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,23 @@
1-
name: Release
1+
name: Create Release on Merge
22

33
on:
4-
push:
5-
branches:
6-
- main
4+
pull_request:
5+
types: [closed]
76

87
jobs:
9-
pypi-publish:
10-
name: upload release to PyPI
8+
release:
9+
if: >
10+
github.event.pull_request.merged == true &&
11+
github.base_ref == 'main' &&
12+
github.head_ref == 'develop'
1113
runs-on: ubuntu-latest
12-
# Specifying a GitHub environment is optional, but strongly encouraged
13-
environment: release
14-
permissions:
15-
# IMPORTANT: this permission is mandatory for trusted publishing
16-
id-token: write
17-
steps:
18-
- uses: actions/checkout@v2
1914

20-
- name: Set up Python ${{ matrix.python-version }}
21-
uses: actions/setup-python@v1
15+
steps:
16+
- name: Create Release
17+
uses: actions/create-release@v1
18+
env:
19+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2220
with:
23-
python-version: ${{ matrix.python-version }}
24-
25-
- name: Install dependencies
26-
shell: bash
27-
run: pip install -r requirements_dev.txt
28-
29-
- name: Build the project
30-
shell: bash
31-
run: python -m build .
32-
33-
- name: Publish package distributions to PyPI
34-
uses: pypa/gh-action-pypi-publish@release/v1
21+
tag_name: ${{ github.event.pull_request.title }}
22+
release_name: ${{ github.event.pull_request.title }}
23+
body: ${{ github.event.pull_request.body }}
Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,50 @@
1-
name: New tests
1+
name: Tests
22

3-
on:
4-
push
3+
on: push
54

65
jobs:
76
build:
8-
97
runs-on: ${{ matrix.os }}
108
strategy:
119
matrix:
1210
os: [ubuntu-latest, windows-latest, macos-latest]
13-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
11+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
1412

1513
steps:
16-
- uses: actions/checkout@v2
17-
- name: Set up Python ${{ matrix.python-version }}
18-
uses: actions/setup-python@v3
19-
with:
14+
- uses: actions/checkout@v4
15+
- name: Set up Python ${{ matrix.python-version }}
16+
uses: actions/setup-python@v5
17+
with:
2018
python-version: ${{ matrix.python-version }}
2119

22-
- name: Install dependencies
23-
shell: bash
24-
run: pip install -r requirements_dev.txt
25-
26-
- name: Install package
27-
run: pip install .
28-
29-
- name: Run tests and show coverage on the command line
30-
run: coverage run --source=locklib --omit="*tests*" -m pytest --cache-clear && coverage report -m --fail-under=100
31-
32-
- name: Upload reports to codecov
33-
env:
34-
CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}}
35-
if: runner.os == 'Linux'
36-
run: |
37-
curl -Os https://uploader.codecov.io/latest/linux/codecov
38-
find . -iregex "codecov.*"
39-
chmod +x codecov
40-
./codecov -t ${CODECOV_TOKEN}
41-
42-
- name: Run tests and show the branch coverage on the command line
43-
run: coverage run --branch --source=locklib --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100
20+
- name: Install dependencies
21+
shell: bash
22+
run: pip install -r requirements_dev.txt
23+
24+
- name: Install package
25+
run: pip install .
26+
27+
- name: Cache pip dependencies
28+
uses: actions/cache@v4
29+
with:
30+
path: ~/.cache/pip
31+
key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
32+
restore-keys: |
33+
${{ runner.os }}-pip-${{ github.workflow }}-
34+
35+
- name: Run tests and show coverage on the command line
36+
run: |
37+
coverage run --source=locklib --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100
38+
coverage xml
39+
40+
- name: Upload coverage to Coveralls
41+
if: runner.os == 'Linux'
42+
env:
43+
COVERALLS_REPO_TOKEN: ${{secrets.COVERALLS_REPO_TOKEN}}
44+
uses: coverallsapp/github-action@v2
45+
with:
46+
format: cobertura
47+
file: coverage.xml
48+
49+
- name: Run tests and show the branch coverage on the command line
50+
run: coverage run --branch --source=locklib --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=100

README.md

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
![logo](https://raw.githubusercontent.com/pomponchik/locklib/develop/docs/assets/logo_5.png)
1+
![logo](https://raw.githubusercontent.com/pomponchik/locklib/develop/docs/assets/logo_7.svg)
22

33
[![Downloads](https://static.pepy.tech/badge/locklib/month)](https://pepy.tech/project/locklib)
44
[![Downloads](https://static.pepy.tech/badge/locklib)](https://pepy.tech/project/locklib)
5-
[![codecov](https://codecov.io/gh/pomponchik/locklib/graph/badge.svg?token=O9G4FD8QFC)](https://codecov.io/gh/pomponchik/locklib)
5+
[![Coverage Status](https://coveralls.io/repos/github/pomponchik/locklib/badge.svg?branch=main)](https://coveralls.io/github/pomponchik/locklib?branch=main)
66
[![Lines of code](https://sloc.xyz/github/pomponchik/locklib/?category=code?)](https://github.com/boyter/scc/)
77
[![Hits-of-Code](https://hitsofcode.com/github/pomponchik/locklib?branch=main)](https://hitsofcode.com/github/pomponchik/locklib/view?branch=main)
88
[![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)
99
[![Python versions](https://img.shields.io/pypi/pyversions/locklib.svg)](https://pypi.python.org/pypi/locklib)
1010
[![PyPI version](https://badge.fury.io/py/locklib.svg)](https://badge.fury.io/py/locklib)
1111
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
1212
[![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)
13+
[![DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/pomponchik/locklib)
1314

1415
It contains several useful additions to the standard thread synchronization tools, such as lock protocols and locks with advanced functionality.
1516

@@ -19,6 +20,7 @@ It contains several useful additions to the standard thread synchronization tool
1920
- [**Installation**](#installation)
2021
- [**Lock protocols**](#lock-protocols)
2122
- [**SmartLock - deadlock is impossible with it**](#smartlock---deadlock-is-impossible-with-it)
23+
- [**Test your locks**](#test-your-locks)
2224

2325

2426
## Installation
@@ -170,3 +172,42 @@ If you want to catch the exception, import this from the `locklib` too:
170172
```python
171173
from locklib import DeadLockError
172174
```
175+
176+
177+
## Test your locks
178+
179+
Sometimes, when testing a code, you may need to detect if some action is taking place inside the lock. How to do this with a minimum of code? There is the `LockTraceWrapper` for this. It is a wrapper around a regular lock, which records it every time the code takes a lock or releases it. At the same time, the functionality of the wrapped lock is fully preserved.
180+
181+
It's easy to create an object of such a lock. Just pass any other lock to the class constructor:
182+
183+
```python
184+
from threading import Lock
185+
from locklib import LockTraceWrapper
186+
187+
lock = LockTraceWrapper(Lock())
188+
```
189+
190+
You can use it in the same way as the wrapped lock:
191+
192+
```python
193+
with lock:
194+
...
195+
```
196+
197+
Anywhere in your program, you can "inform" the lock that the action you need is being performed here:
198+
199+
```python
200+
lock.notify('event_name')
201+
```
202+
203+
And! Now you can easily identify if there were cases when an event with this identifier did not occur under the mutex. To do this, use the `was_event_locked` method:
204+
205+
```python
206+
lock.was_event_locked('event_name')
207+
```
208+
209+
If the `notify` method was called with the same parameter only when the lock activated, it will return `True`. If not, that is, if there was at least one case when the c method was called with such an identifier without an activated mutex, `False` will be returned.
210+
211+
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.
212+
213+
> ⚠️ 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.

0 commit comments

Comments
 (0)