Skip to content

Commit 4f3d148

Browse files
committed
Implement code changes to enhance functionality and improve performance
1 parent 4710dd6 commit 4f3d148

File tree

5 files changed

+614
-33
lines changed

5 files changed

+614
-33
lines changed

.vscode/settings.json

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"python.testing.unittestArgs": [
3+
"-v",
4+
"-s",
5+
"./src",
6+
"-p",
7+
"test_*.py",
8+
"*_test.py"
9+
],
10+
"python.testing.pytestEnabled": true,
11+
"python.testing.unittestEnabled": false,
12+
"python.testing.pytestArgs": [
13+
"--rootdir", "${workspaceFolder}/src",
14+
"--ignore-glob", "_SS*",
15+
"--ignore-glob", "src/deployment/api/_shared_od*",
16+
"--ignore-glob", "GUI",
17+
"--cache-clear"
18+
],
19+
"python.analysis.typeCheckingMode": "strict",
20+
"python.analysis.include": [
21+
"./src/**",
22+
],
23+
"python.analysis.exclude": [
24+
"**/_SS**",
25+
"**/dist/**",
26+
"**/build/**",
27+
"**/.git/**",
28+
"**/.hg/**",
29+
"**/.svn/**",
30+
"**/.vscode/**",
31+
"**/.history/**",
32+
"**/CVS/**",
33+
"**/.cache/**",
34+
"**/venv/**",
35+
"**/.venv/**",
36+
"**/env/**",
37+
"**/.env/**",
38+
"**/site-packages/**",
39+
],
40+
"python.analysis.diagnosticSeverityOverrides": {
41+
"reportUnusedImport": "information",
42+
"reportUnusedVariable": "information",
43+
"reportPrivateUsage": "warning",
44+
"reportMissingTypeStubs": "information",
45+
"reportConstantRedefinition": "information",
46+
"reportMissingTypeArgument": "information",
47+
"reportMissingParameterType": "warning",
48+
"reportUnknownArgumentType": "warning",
49+
"reportUnnecessaryCast": "warning",
50+
"reportUnknownMemberType": "warning",
51+
"reportUnknownVariableType": "warning",
52+
"reportUnknownParameterType": "warning"
53+
},
54+
"python.analysis.extraPaths": [
55+
"./src",
56+
"./src/data",
57+
"./src/deployment",
58+
"./src/features",
59+
"./src/models",
60+
"./src/integration",
61+
"./src/visualisation",
62+
],
63+
"python.autoComplete.extraPaths": [
64+
"./src",
65+
"./src/data",
66+
"./src/deployment",
67+
"./src/features",
68+
"./src/models",
69+
"./src/integration",
70+
"./src/visualisation",
71+
],
72+
"search.followSymlinks": false,
73+
"search.exclude": {
74+
"**/node_modules": true,
75+
"**/bower_components": true,
76+
"**/*.code-search": true,
77+
"**/*.drawio": true,
78+
"**/_SS/**": true,
79+
"**.venv/**": true,
80+
},
81+
"files.exclude": {
82+
"**/.pytest_cache": true,
83+
"**/.*": {
84+
"when": "$(basename).isDirectory"
85+
}
86+
}
87+
}

concurrent_collections/concurrent_dict.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import threading
2-
from typing import Any, Callable, Dict, Iterator, List, Optional, TypeVar
2+
from typing import Any, Callable, Dict, Iterator, List, Optional, TypeVar, Generic
33
import warnings
44
import sys
55

66
T = TypeVar('T')
77
K = TypeVar('K')
88
V = TypeVar('V')
99

10-
class ConcurrentDictionary:
10+
class ConcurrentDictionary(Generic[K, V]):
1111
"""
1212
A thread-safe dictionary implementation using a re-entrant lock.
1313
All operations that mutate or access the dictionary are protected.
@@ -64,7 +64,13 @@ def update_atomic(self, key: K, func: Callable[[V], V]) -> None:
6464
d.update_atomic('x', lambda v: v + 1)
6565
"""
6666
with self._lock:
67-
self._dict[key] = func(self._dict[key])
67+
if key in self._dict:
68+
old_value = self._dict[key]
69+
new_value = func(old_value)
70+
self._dict[key] = new_value
71+
else:
72+
# If the key does not exist, we can set it directly
73+
self._dict[key] = func(None) # type: ignore
6874

6975

7076
def pop(self, key: K, default: Optional[V] = None) -> V:

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ authors = [{ name = "Alessio Lombardi", email = "[email protected]" }]
1010
license = { file = "MIT" }
1111
readme = "README.md"
1212
requires-python = ">=3.6"
13-
dependencies = []
13+
dependencies = [
14+
"pytest>=7.0.1",
15+
]
1416

1517
[tool.setuptools]
16-
packages = ["concurrent_collections"]
18+
packages = ["concurrent_collections"]

tests/concurrent_dict_test.py

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@
66
import threading
77
import time
88
from concurrent_collections import ConcurrentDictionary
9+
import pytest
910

1011

1112
def test_concurrentdictionary_update_thread_safe():
12-
D = ConcurrentDictionary({'x': 0})
13+
dic : ConcurrentDictionary[str, int] = ConcurrentDictionary()
14+
dic["x"] = 0
1315
def worker():
1416
for _ in range(10000):
15-
old = D['x']
17+
old = dic['x']
1618
time.sleep(0.00001)
1719
# D.__setitem__('x', old + 1)
18-
D.update_atomic("x", lambda v: v + 1)
20+
dic.update_atomic("x", lambda v: v + 1)
1921

2022
threads = [threading.Thread(target=worker) for _ in range(8)]
2123
for t in threads:
@@ -24,12 +26,12 @@ def worker():
2426
t.join()
2527

2628
# The expected value is 8 * 10000 = 80000
27-
assert D['x'] == 80000, f"ConcurrentDictionary should be thread-safe, got {D['x']}"
29+
assert dic['x'] == 80000, f"ConcurrentDictionary should be thread-safe, got {dic['x']}"
2830

2931

3032
def test_concurrentdictionary_setdefault_thread_safe():
31-
D = ConcurrentDictionary()
32-
errors = []
33+
D : ConcurrentDictionary[str,int] = ConcurrentDictionary()
34+
errors : list[Exception] = []
3335

3436
def worker():
3537
for _ in range(10000):
@@ -49,8 +51,8 @@ def worker():
4951

5052

5153
def test_concurrentdictionary_pop_thread_safe():
52-
D = ConcurrentDictionary({'x': 0})
53-
errors = []
54+
D : ConcurrentDictionary[str,int] = ConcurrentDictionary({'x': 0})
55+
errors : list[Exception] = []
5456

5557
def worker():
5658
for _ in range(1000):
@@ -71,8 +73,8 @@ def worker():
7173

7274

7375
def test_concurrentdictionary_clear_thread_safe():
74-
D = ConcurrentDictionary({i: i for i in range(100)})
75-
errors = []
76+
D : ConcurrentDictionary[str,int] = ConcurrentDictionary({i: i for i in range(100)})
77+
errors : list[Exception] = []
7678

7779
def worker():
7880
for _ in range(100):
@@ -90,23 +92,7 @@ def worker():
9092

9193
# No thread safety errors should occur
9294
assert not errors, f"Thread safety errors occurred: {errors}"
95+
9396

94-
9597
if __name__ == "__main__":
96-
import types
97-
98-
# Collect all functions in globals() that start with 'test_' and are functions
99-
test_functions = [
100-
func for name, func in globals().items()
101-
if name.startswith("test_") and isinstance(func, types.FunctionType)
102-
]
103-
failed = 0
104-
for func in test_functions:
105-
try:
106-
print(f"Running {func.__name__} ...")
107-
func()
108-
except Exception as e:
109-
failed += 1
110-
print(f"***\nFAILED: {func.__name__}: {e}\n***")
111-
112-
print(f"\n{len(test_functions) - failed} passed, {failed} failed.")
98+
pytest.main([__file__])

0 commit comments

Comments
 (0)