cafs-cache-cdn-client 1.0.6__tar.gz → 1.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (18) hide show
  1. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/PKG-INFO +7 -6
  2. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/README.md +5 -4
  3. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/__init__.py +1 -0
  4. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/blob/utils.py +3 -0
  5. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/client.py +22 -11
  6. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/client.py +4 -2
  7. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/file_utils.py +3 -2
  8. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/pyproject.toml +2 -2
  9. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/README.md +0 -0
  10. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/__init__.py +0 -0
  11. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/blob/__init__.py +0 -0
  12. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/blob/hash_.py +0 -0
  13. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/blob/package.py +0 -0
  14. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/exceptions.py +0 -0
  15. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/cafs/types.py +0 -0
  16. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/repo/__init__.py +0 -0
  17. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/repo/client.py +0 -0
  18. {cafs_cache_cdn_client-1.0.6 → cafs_cache_cdn_client-1.0.8}/cafs_cache_cdn_client/repo/datatypes.py +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: cafs-cache-cdn-client
3
- Version: 1.0.6
3
+ Version: 1.0.8
4
4
  Summary: Async Cache CDN client implementation
5
5
  Keywords: cafs,cache
6
6
  Author: Konstantin Belov
7
7
  Author-email: k.belov@gaijin.team
8
8
  Requires-Python: >=3.11,<4.0
9
9
  Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Development Status :: 4 - Beta
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
@@ -38,7 +38,7 @@ pip install cafs-cache-cdn-client
38
38
  import asyncio
39
39
  import logging
40
40
  from pathlib import Path
41
- from cafs_cache_cdn_client import CacheCdnClient
41
+ from cafs_cache_cdn_client import CacheCdnClient, CompressionT
42
42
 
43
43
  # Configure logging to see detailed operation information
44
44
  logging.basicConfig(level=logging.DEBUG)
@@ -54,10 +54,11 @@ async def main():
54
54
 
55
55
  # Use as an async context manager to ensure proper resource cleanup
56
56
  async with client:
57
- # Push a local directory to cache with a 2-hour TTL
57
+ # Push a local directory to cache with a 2-hour TTL and preferred ZSTD compression
58
58
  await client.push('project_name', 'build_artifacts',
59
59
  '/path/to/build/output', ttl_hours=2,
60
- comment='Build artifacts from CI run #123')
60
+ comment='Build artifacts from CI run #123',
61
+ compression=CompressionT.ZSTD)
61
62
 
62
63
  # Check if a reference exists
63
64
  exists = await client.check('project_name', 'build_artifacts')
@@ -90,7 +91,7 @@ if __name__ == '__main__':
90
91
  - `connection_per_cafs_server`: Number of concurrent connections per CAFS server
91
92
 
92
93
  - **Methods**:
93
- - `push(repo: str, ref: str, directory: Path | str, ttl_hours: int = 0, comment: str | None = None)` - Push a local directory to cache
94
+ - `push(repo: str, ref: str, directory: Path | str, ttl_hours: int = 0, comment: str | None = None, compression: CompressionT = CompressionT.NONE)` - Push a local directory to cache
94
95
  - `pull(repo: str, ref: str, directory: Path | str)` - Pull cached content to a local directory
95
96
  - `check(repo: str, ref: str) -> bool` - Check if a reference exists
96
97
  - `tag(repo: str, ref: str, tag: str)` - Create a tag for a reference
@@ -18,7 +18,7 @@ pip install cafs-cache-cdn-client
18
18
  import asyncio
19
19
  import logging
20
20
  from pathlib import Path
21
- from cafs_cache_cdn_client import CacheCdnClient
21
+ from cafs_cache_cdn_client import CacheCdnClient, CompressionT
22
22
 
23
23
  # Configure logging to see detailed operation information
24
24
  logging.basicConfig(level=logging.DEBUG)
@@ -34,10 +34,11 @@ async def main():
34
34
 
35
35
  # Use as an async context manager to ensure proper resource cleanup
36
36
  async with client:
37
- # Push a local directory to cache with a 2-hour TTL
37
+ # Push a local directory to cache with a 2-hour TTL and preferred ZSTD compression
38
38
  await client.push('project_name', 'build_artifacts',
39
39
  '/path/to/build/output', ttl_hours=2,
40
- comment='Build artifacts from CI run #123')
40
+ comment='Build artifacts from CI run #123',
41
+ compression=CompressionT.ZSTD)
41
42
 
42
43
  # Check if a reference exists
43
44
  exists = await client.check('project_name', 'build_artifacts')
@@ -70,7 +71,7 @@ if __name__ == '__main__':
70
71
  - `connection_per_cafs_server`: Number of concurrent connections per CAFS server
71
72
 
72
73
  - **Methods**:
73
- - `push(repo: str, ref: str, directory: Path | str, ttl_hours: int = 0, comment: str | None = None)` - Push a local directory to cache
74
+ - `push(repo: str, ref: str, directory: Path | str, ttl_hours: int = 0, comment: str | None = None, compression: CompressionT = CompressionT.NONE)` - Push a local directory to cache
74
75
  - `pull(repo: str, ref: str, directory: Path | str)` - Pull cached content to a local directory
75
76
  - `check(repo: str, ref: str) -> bool` - Check if a reference exists
76
77
  - `tag(repo: str, ref: str, tag: str)` - Create a tag for a reference
@@ -1 +1,2 @@
1
+ from .cafs import CompressionT
1
2
  from .client import CacheCdnClient
@@ -28,6 +28,9 @@ def is_file_already_compressed(file_path: Path) -> bool:
28
28
  def choose_compression(
29
29
  file_path: Path, preferred_compression: CompressionT = CompressionT.NONE
30
30
  ) -> CompressionT:
31
+ if preferred_compression == CompressionT.NONE:
32
+ return preferred_compression
33
+
31
34
  if file_path.stat().st_size < MINIMAL_COMPRESSION_SIZE:
32
35
  return CompressionT.NONE
33
36
 
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- from asyncio import Queue, QueueShutDown
3
2
  from collections.abc import (
4
3
  AsyncIterator,
5
4
  Callable,
@@ -282,7 +281,8 @@ class ConnectionPool:
282
281
 
283
282
  _lock: asyncio.Lock
284
283
  _connections: set[CAFSConnection]
285
- _connection_queue: Queue[CAFSConnection]
284
+ _connection_queue: asyncio.Queue[CAFSConnection]
285
+ _close_event: asyncio.Event
286
286
 
287
287
  def __init__(
288
288
  self,
@@ -297,8 +297,9 @@ class ConnectionPool:
297
297
  self.connection_per_server = connection_per_server
298
298
 
299
299
  self._connections = set()
300
- self._connection_queue = Queue()
300
+ self._connection_queue = asyncio.Queue()
301
301
  self._lock = asyncio.Lock()
302
+ self._close_event = asyncio.Event()
302
303
 
303
304
  async def get_connection_count(self) -> int:
304
305
  async with self._lock:
@@ -315,10 +316,21 @@ class ConnectionPool:
315
316
  await self._connection_queue.put(conn)
316
317
 
317
318
  async def _get_connection(self) -> CAFSConnection:
318
- try:
319
- return await self._connection_queue.get()
320
- except QueueShutDown as err:
321
- raise EmptyConnectionPoolError() from err
319
+ if self._close_event.is_set():
320
+ raise EmptyConnectionPoolError()
321
+ get_task = asyncio.create_task(self._connection_queue.get())
322
+ close_task = asyncio.create_task(self._close_event.wait())
323
+ _, pending = await asyncio.wait(
324
+ [get_task, close_task], return_when=asyncio.FIRST_COMPLETED
325
+ )
326
+
327
+ for task in pending:
328
+ task.cancel()
329
+
330
+ if get_task in pending:
331
+ raise EmptyConnectionPoolError()
332
+
333
+ return get_task.result()
322
334
 
323
335
  async def _release_connection(self, conn: CAFSConnection) -> None:
324
336
  await self._connection_queue.put(conn)
@@ -328,14 +340,15 @@ class ConnectionPool:
328
340
  async with self._lock:
329
341
  self._connections.remove(conn)
330
342
  if not self._connections:
331
- self._connection_queue.shutdown(immediate=True)
343
+ self._close_event.set()
332
344
 
333
345
  async def close(self) -> None:
334
346
  async with self._lock:
335
- self._connection_queue.shutdown(immediate=True)
347
+ self._close_event.set()
336
348
  for conn in self._connections:
337
349
  if conn.is_connected:
338
350
  await conn.disconnect()
351
+ self._connections.clear()
339
352
 
340
353
  @asynccontextmanager
341
354
  async def connection(self) -> AsyncIterator[CAFSConnection]:
@@ -428,7 +441,6 @@ class CAFSClient:
428
441
  stop_event = asyncio.Event()
429
442
  workers = [asyncio.create_task(worker(stop_event)) for _ in range(len(blobs))]
430
443
  errors = await asyncio.gather(*workers, return_exceptions=True)
431
- files_queue.shutdown(immediate=True)
432
444
 
433
445
  for err in errors:
434
446
  if isinstance(err, Exception):
@@ -508,7 +520,6 @@ class CAFSClient:
508
520
  asyncio.create_task(worker(stop_event)) for _ in range(max_concurrent)
509
521
  ]
510
522
  errors = await asyncio.gather(*workers, return_exceptions=True)
511
- files_queue.shutdown(immediate=True)
512
523
 
513
524
  for err in errors:
514
525
  if isinstance(err, Exception):
@@ -7,7 +7,7 @@ from typing import Any, Self, TypeVar
7
7
 
8
8
  import aiofiles.os as aio_os
9
9
 
10
- from cafs_cache_cdn_client.cafs import CAFSClient
10
+ from cafs_cache_cdn_client.cafs import CAFSClient, CompressionT
11
11
  from cafs_cache_cdn_client.file_utils import (
12
12
  LocalFile,
13
13
  compare_file_lists,
@@ -64,6 +64,7 @@ class CacheCdnClient:
64
64
  directory: Path | str,
65
65
  ttl_hours: int = 0,
66
66
  comment: str | None = None,
67
+ compression: CompressionT = CompressionT.NONE,
67
68
  ) -> None:
68
69
  if isinstance(directory, str):
69
70
  directory = Path(directory)
@@ -71,7 +72,8 @@ class CacheCdnClient:
71
72
  raise ValueError(f'{directory} is not a directory')
72
73
  files = walk(directory)
73
74
  hashes = await self._cafs_client.stream_batch(
74
- [directory / file.path for file in files]
75
+ [directory / file.path for file in files],
76
+ compression=compression,
75
77
  )
76
78
  await self._repo_client.post_ref_info(
77
79
  repo,
@@ -40,9 +40,10 @@ class LocalFile:
40
40
 
41
41
  def walk(directory: Path) -> list[LocalFile]:
42
42
  results = []
43
- for root, _, files in directory.walk():
43
+ for root, _, files in os.walk(str(directory)):
44
+ root_ = Path(root)
44
45
  for file in files:
45
- file_ = root / file
46
+ file_ = root_ / file
46
47
  file_stat = file_.stat()
47
48
  results.append(
48
49
  LocalFile(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cafs-cache-cdn-client"
3
- version = "1.0.6"
3
+ version = "1.0.8"
4
4
  description = "Async Cache CDN client implementation"
5
5
  authors = [
6
6
  { name = "Konstantin Belov", "email" = "k.belov@gaijin.team" },
@@ -9,7 +9,7 @@ readme = "README.md"
9
9
  keywords = ["cafs", "cache"]
10
10
  classifiers = [
11
11
  "License :: OSI Approved :: MIT License",
12
- "Development Status :: 5 - Production/Stable",
12
+ "Development Status :: 4 - Beta",
13
13
  "Programming Language :: Python :: 3.11",
14
14
  "Programming Language :: Python :: 3.12",
15
15
  "Programming Language :: Python :: 3.13",