glow 0.15.7__tar.gz → 0.15.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 (59) hide show
  1. {glow-0.15.7 → glow-0.15.8}/PKG-INFO +15 -14
  2. {glow-0.15.7 → glow-0.15.8}/pyproject.toml +7 -6
  3. {glow-0.15.7 → glow-0.15.8}/src/glow/_parallel.py +22 -24
  4. {glow-0.15.7 → glow-0.15.8}/src/glow/_parallel.pyi +1 -1
  5. {glow-0.15.7 → glow-0.15.8}/src/glow/_thread_quota.py +16 -2
  6. {glow-0.15.7 → glow-0.15.8}/.gitignore +0 -0
  7. {glow-0.15.7 → glow-0.15.8}/LICENSE +0 -0
  8. {glow-0.15.7 → glow-0.15.8}/README.md +0 -0
  9. {glow-0.15.7 → glow-0.15.8}/src/glow/__init__.py +0 -0
  10. {glow-0.15.7 → glow-0.15.8}/src/glow/_array.py +0 -0
  11. {glow-0.15.7 → glow-0.15.8}/src/glow/_async.py +0 -0
  12. {glow-0.15.7 → glow-0.15.8}/src/glow/_async.pyi +0 -0
  13. {glow-0.15.7 → glow-0.15.8}/src/glow/_cache.py +0 -0
  14. {glow-0.15.7 → glow-0.15.8}/src/glow/_cache.pyi +0 -0
  15. {glow-0.15.7 → glow-0.15.8}/src/glow/_concurrency.py +0 -0
  16. {glow-0.15.7 → glow-0.15.8}/src/glow/_concurrency.pyi +0 -0
  17. {glow-0.15.7 → glow-0.15.8}/src/glow/_coro.py +0 -0
  18. {glow-0.15.7 → glow-0.15.8}/src/glow/_debug.py +0 -0
  19. {glow-0.15.7 → glow-0.15.8}/src/glow/_dev.py +0 -0
  20. {glow-0.15.7 → glow-0.15.8}/src/glow/_futures.py +0 -0
  21. {glow-0.15.7 → glow-0.15.8}/src/glow/_ic.py +0 -0
  22. {glow-0.15.7 → glow-0.15.8}/src/glow/_import_hook.py +0 -0
  23. {glow-0.15.7 → glow-0.15.8}/src/glow/_imutil.py +0 -0
  24. {glow-0.15.7 → glow-0.15.8}/src/glow/_keys.py +0 -0
  25. {glow-0.15.7 → glow-0.15.8}/src/glow/_logging.py +0 -0
  26. {glow-0.15.7 → glow-0.15.8}/src/glow/_more.py +0 -0
  27. {glow-0.15.7 → glow-0.15.8}/src/glow/_patch_len.py +0 -0
  28. {glow-0.15.7 → glow-0.15.8}/src/glow/_patch_print.py +0 -0
  29. {glow-0.15.7 → glow-0.15.8}/src/glow/_patch_scipy.py +0 -0
  30. {glow-0.15.7 → glow-0.15.8}/src/glow/_profile.py +0 -0
  31. {glow-0.15.7 → glow-0.15.8}/src/glow/_profile.pyi +0 -0
  32. {glow-0.15.7 → glow-0.15.8}/src/glow/_reduction.py +0 -0
  33. {glow-0.15.7 → glow-0.15.8}/src/glow/_repr.py +0 -0
  34. {glow-0.15.7 → glow-0.15.8}/src/glow/_reusable.py +0 -0
  35. {glow-0.15.7 → glow-0.15.8}/src/glow/_sizeof.py +0 -0
  36. {glow-0.15.7 → glow-0.15.8}/src/glow/_streams.py +0 -0
  37. {glow-0.15.7 → glow-0.15.8}/src/glow/_types.py +0 -0
  38. {glow-0.15.7 → glow-0.15.8}/src/glow/_uuid.py +0 -0
  39. {glow-0.15.7 → glow-0.15.8}/src/glow/_wrap.py +0 -0
  40. {glow-0.15.7 → glow-0.15.8}/src/glow/api/__init__.py +0 -0
  41. {glow-0.15.7 → glow-0.15.8}/src/glow/api/config.py +0 -0
  42. {glow-0.15.7 → glow-0.15.8}/src/glow/api/exporting.py +0 -0
  43. {glow-0.15.7 → glow-0.15.8}/src/glow/cli.py +0 -0
  44. {glow-0.15.7 → glow-0.15.8}/src/glow/cli.pyi +0 -0
  45. {glow-0.15.7 → glow-0.15.8}/src/glow/io/__init__.py +0 -0
  46. {glow-0.15.7 → glow-0.15.8}/src/glow/io/_sound.py +0 -0
  47. {glow-0.15.7 → glow-0.15.8}/src/glow/io/_svg.py +0 -0
  48. {glow-0.15.7 → glow-0.15.8}/src/glow/py.typed +0 -0
  49. {glow-0.15.7 → glow-0.15.8}/test/__init__.py +0 -0
  50. {glow-0.15.7 → glow-0.15.8}/test/test_api.py +0 -0
  51. {glow-0.15.7 → glow-0.15.8}/test/test_batch.py +0 -0
  52. {glow-0.15.7 → glow-0.15.8}/test/test_buffered.py +0 -0
  53. {glow-0.15.7 → glow-0.15.8}/test/test_cli.py +0 -0
  54. {glow-0.15.7 → glow-0.15.8}/test/test_iter.py +0 -0
  55. {glow-0.15.7 → glow-0.15.8}/test/test_shm.py +0 -0
  56. {glow-0.15.7 → glow-0.15.8}/test/test_thread_pool.py +0 -0
  57. {glow-0.15.7 → glow-0.15.8}/test/test_timed.py +0 -0
  58. {glow-0.15.7 → glow-0.15.8}/test/test_timer.py +0 -0
  59. {glow-0.15.7 → glow-0.15.8}/test/test_uuid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: glow
3
- Version: 0.15.7
3
+ Version: 0.15.8
4
4
  Summary: Functional Python tools
5
5
  Project-URL: homepage, https://github.com/arquolo/glow
6
6
  Author-email: Paul Maevskikh <arquolo@gmail.com>
@@ -33,7 +33,8 @@ Classifier: Operating System :: OS Independent
33
33
  Classifier: Programming Language :: Python :: 3
34
34
  Classifier: Programming Language :: Python :: 3.12
35
35
  Classifier: Programming Language :: Python :: 3.13
36
- Requires-Python: >=3.12
36
+ Classifier: Programming Language :: Python :: 3.14
37
+ Requires-Python: <3.15,>=3.12
37
38
  Requires-Dist: loguru
38
39
  Requires-Dist: loky~=3.1
39
40
  Requires-Dist: lxml
@@ -52,7 +53,7 @@ Requires-Dist: pygments; extra == 'all'
52
53
  Requires-Dist: sounddevice; extra == 'all'
53
54
  Requires-Dist: soundfile; extra == 'all'
54
55
  Provides-Extra: dev
55
- Requires-Dist: black~=25.1; extra == 'dev'
56
+ Requires-Dist: black~=26.1; extra == 'dev'
56
57
  Requires-Dist: flake8-alphabetize; extra == 'dev'
57
58
  Requires-Dist: flake8-pie; extra == 'dev'
58
59
  Requires-Dist: flake8-pyi; extra == 'dev'
@@ -60,34 +61,34 @@ Requires-Dist: flake8-pyproject; extra == 'dev'
60
61
  Requires-Dist: flake8-simplify; extra == 'dev'
61
62
  Requires-Dist: flake8~=7.0; extra == 'dev'
62
63
  Requires-Dist: isort; extra == 'dev'
63
- Requires-Dist: mypy~=1.16; extra == 'dev'
64
+ Requires-Dist: mypy~=1.19; extra == 'dev'
64
65
  Requires-Dist: pytest-asyncio; extra == 'dev'
65
- Requires-Dist: pytest~=8.0; extra == 'dev'
66
- Requires-Dist: ruff~=0.12.2; extra == 'dev'
66
+ Requires-Dist: pytest~=9.0; extra == 'dev'
67
+ Requires-Dist: ruff~=0.15.0; extra == 'dev'
67
68
  Provides-Extra: dev-core
68
- Requires-Dist: black~=25.1; extra == 'dev-core'
69
+ Requires-Dist: black~=26.1; extra == 'dev-core'
69
70
  Requires-Dist: flake8-pie; extra == 'dev-core'
70
71
  Requires-Dist: flake8-pyi; extra == 'dev-core'
71
72
  Requires-Dist: flake8-pyproject; extra == 'dev-core'
72
73
  Requires-Dist: flake8-simplify; extra == 'dev-core'
73
74
  Requires-Dist: flake8~=7.0; extra == 'dev-core'
74
75
  Requires-Dist: isort; extra == 'dev-core'
75
- Requires-Dist: mypy~=1.16; extra == 'dev-core'
76
+ Requires-Dist: mypy~=1.19; extra == 'dev-core'
76
77
  Requires-Dist: pytest-asyncio; extra == 'dev-core'
77
- Requires-Dist: pytest~=8.0; extra == 'dev-core'
78
- Requires-Dist: ruff~=0.12.2; extra == 'dev-core'
78
+ Requires-Dist: pytest~=9.0; extra == 'dev-core'
79
+ Requires-Dist: ruff~=0.15.0; extra == 'dev-core'
79
80
  Provides-Extra: dev-wemake
80
- Requires-Dist: black~=25.1; extra == 'dev-wemake'
81
+ Requires-Dist: black~=26.1; extra == 'dev-wemake'
81
82
  Requires-Dist: flake8-pie; extra == 'dev-wemake'
82
83
  Requires-Dist: flake8-pyi; extra == 'dev-wemake'
83
84
  Requires-Dist: flake8-pyproject; extra == 'dev-wemake'
84
85
  Requires-Dist: flake8-simplify; extra == 'dev-wemake'
85
86
  Requires-Dist: flake8~=7.0; extra == 'dev-wemake'
86
87
  Requires-Dist: isort; extra == 'dev-wemake'
87
- Requires-Dist: mypy~=1.16; extra == 'dev-wemake'
88
+ Requires-Dist: mypy~=1.19; extra == 'dev-wemake'
88
89
  Requires-Dist: pytest-asyncio; extra == 'dev-wemake'
89
- Requires-Dist: pytest~=8.0; extra == 'dev-wemake'
90
- Requires-Dist: ruff~=0.12.2; extra == 'dev-wemake'
90
+ Requires-Dist: pytest~=9.0; extra == 'dev-wemake'
91
+ Requires-Dist: ruff~=0.15.0; extra == 'dev-wemake'
91
92
  Requires-Dist: wemake-python-styleguide~=1.3.0; extra == 'dev-wemake'
92
93
  Provides-Extra: ic
93
94
  Requires-Dist: asttokens; extra == 'ic'
@@ -7,10 +7,10 @@ only-packages = true
7
7
 
8
8
  [project]
9
9
  name = "glow"
10
- version = "0.15.7"
10
+ version = "0.15.8"
11
11
  description = "Functional Python tools"
12
12
  readme = "README.md"
13
- requires-python = ">=3.12"
13
+ requires-python = ">=3.12, <3.15"
14
14
  license = {file = "LICENSE"}
15
15
  keywords = []
16
16
  authors = [
@@ -26,6 +26,7 @@ classifiers = [
26
26
  "Programming Language :: Python :: 3",
27
27
  "Programming Language :: Python :: 3.12",
28
28
  "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: 3.14",
29
30
  ]
30
31
  dependencies = [
31
32
  "loguru",
@@ -62,17 +63,17 @@ all = [
62
63
  "matplotlib",
63
64
  ]
64
65
  dev-core = [
65
- "black~=25.1",
66
+ "black~=26.1",
66
67
  "flake8~=7.0",
67
68
  "flake8-pie",
68
69
  "flake8-pyi",
69
70
  "flake8-pyproject",
70
71
  "flake8-simplify",
71
72
  "isort",
72
- "mypy~=1.16",
73
- "pytest~=8.0",
73
+ "mypy~=1.19",
74
+ "pytest~=9.0",
74
75
  "pytest-asyncio",
75
- "ruff~=0.12.2",
76
+ "ruff~=0.15.0",
76
77
  ]
77
78
  dev = [
78
79
  "glow[dev-core]",
@@ -441,10 +441,10 @@ def _enqueue[T](
441
441
  return sched_iter, q_get
442
442
 
443
443
 
444
- def _prefetch(s: ExitStack, sched_iter: Iterator, prefetch: int | None) -> int:
444
+ def _prefetch(s: ExitStack, sched_iter: Iterator, count: int | None) -> int:
445
445
  try:
446
- # Fetch up to `prefetch` tasks to pre-fill `q`
447
- qsize = ilen(islice(sched_iter, prefetch))
446
+ # Fetch up to `count` tasks to pre-fill `q`
447
+ qsize = ilen(islice(sched_iter, count))
448
448
  except BaseException:
449
449
  # Unwind stack here on an error
450
450
  s.close()
@@ -469,7 +469,7 @@ def starmap_n[T](
469
469
  prefetch: int | None = 2,
470
470
  mp: bool = False,
471
471
  chunksize: int | None = None,
472
- order: bool = True,
472
+ unordered: bool = False,
473
473
  ) -> Iterator[T]:
474
474
  """Equivalent to itertools.starmap(fn, iterable).
475
475
 
@@ -478,29 +478,27 @@ def starmap_n[T](
478
478
 
479
479
  Options:
480
480
  - workers - Count of workers, by default all hardware threads are occupied.
481
- - prefetch - Extra count of scheduled jobs, if not set equals to infinity.
481
+ - prefetch - Count of extra jobs to schedule over N workers.
482
+ Helps with CPU stalls in ordered mode.
483
+ Increase if job execution time is highly variable.
482
484
  - mp - Whether use processes or threads.
483
485
  - chunksize - The size of the chunks the iterable will be broken into
484
- before being passed to a processes. Estimated automatically.
486
+ before being passed to a processes.
487
+ Estimated automatically.
485
488
  Ignored when threads are used.
486
- - order - Whether keep results order, or ignore it to increase performance.
489
+ - unordered - Retrieve results in order of completion or in original order.
490
+ In this mode `prefetch` is meaningless, because when some job became done
491
+ it yielded immediately releasing buffer for new job to schedule.
492
+ So no CPU stalls.
487
493
 
488
494
  Unlike multiprocessing.Pool or concurrent.futures.Executor this one:
489
495
  - never deadlocks on any exception or Ctrl-C interruption.
490
- - accepts infinite iterables due to lazy task creation (option prefetch).
496
+ - accepts infinite iterables due to lazy task creation.
491
497
  - has single interface for both threads and processes.
492
498
  - TODO: serializes array-like data using out-of-band Pickle 5 buffers.
493
- - before first `__next__` call it submits at most `prefetch` jobs
494
- to warmup pool of workers.
495
-
496
- Notes:
497
- - To reduce latency set order to False, order of results will be arbitrary.
498
- - To increase CPU usage increase prefetch or set it to None.
499
- - In terms of CPU usage there's no difference between
500
- prefetch=None and order=False, so choose wisely.
501
- - Setting order to False makes no use of prefetch more than 0.
502
-
503
- TODO: replace `order=True` with `unordered=False`
499
+ - call immediately creates pool ready to yield results
500
+ (which could take some time cause of serialization for multiprocessing),
501
+ so first `__next__` runs on warmed up pool.
504
502
  """
505
503
  if max_workers is None:
506
504
  max_workers = max_cpu_count(_NUM_CPUS, mp=mp)
@@ -512,11 +510,11 @@ def starmap_n[T](
512
510
  msg = 'With multiprocessing either chunksize or prefetch should be set'
513
511
  raise ValueError(msg)
514
512
 
515
- if prefetch is not None:
513
+ if unordered:
514
+ prefetch = max(max_workers, 1)
515
+ elif prefetch is not None:
516
516
  prefetch = max(prefetch + max_workers, 1)
517
517
 
518
- unordered = not order
519
-
520
518
  it = iter(iterable)
521
519
  s = ExitStack()
522
520
  submit = s.enter_context(get_executor(max_workers, mp=mp)).submit
@@ -560,7 +558,7 @@ def map_n[T](
560
558
  prefetch: int | None = 2,
561
559
  mp: bool = False,
562
560
  chunksize: int | None = None,
563
- order: bool = True,
561
+ unordered: bool = False,
564
562
  ) -> Iterator[T]:
565
563
  """Return iterator equivalent to map(func, *iterables).
566
564
 
@@ -576,7 +574,7 @@ def map_n[T](
576
574
  prefetch=prefetch,
577
575
  mp=mp,
578
576
  chunksize=chunksize,
579
- order=order,
577
+ unordered=unordered,
580
578
  )
581
579
 
582
580
 
@@ -10,7 +10,7 @@ class _MapKwargs(TypedDict, total=False):
10
10
  chunksize: int | None
11
11
 
12
12
  class _MapIterKwargs(_MapKwargs, total=False):
13
- order: bool
13
+ unordered: bool
14
14
 
15
15
  def max_cpu_count(upper_bound: int = ..., *, mp: bool = ...) -> int: ...
16
16
  def get_executor(
@@ -8,6 +8,7 @@
8
8
  __all__ = ['ThreadQuota']
9
9
 
10
10
  import os
11
+ import sys
11
12
  from collections import deque
12
13
  from collections.abc import Callable
13
14
  from concurrent.futures import Executor, Future
@@ -18,6 +19,13 @@ from threading import _register_atexit # type: ignore[attr-defined]
18
19
  from threading import Lock, Thread
19
20
  from weakref import WeakSet
20
21
 
22
+ if sys.version_info >= (3, 14):
23
+ from concurrent.futures.thread import WorkerContext
24
+
25
+ _worker_ctx = WorkerContext(lambda: None, ())
26
+ else:
27
+ _worker_ctx = None
28
+
21
29
  # TODO: investigate hangups when _TIMEOUT <= .01
22
30
  _TIMEOUT = 1
23
31
  _MIN_IDLE = os.cpu_count() or 1
@@ -64,7 +72,10 @@ def _worker(q: _Pipe) -> None:
64
72
  try:
65
73
  while executor := _safe_call(q.get, timeout=_TIMEOUT):
66
74
  while work_item := _safe_call(executor._work_queue.popleft):
67
- work_item.run() # Process task
75
+ if sys.version_info >= (3, 14):
76
+ work_item.run(_worker_ctx) # Process task
77
+ else:
78
+ work_item.run()
68
79
  if _shutdown:
69
80
  executor._shutdown = True
70
81
  return
@@ -114,7 +125,10 @@ class ThreadQuota(Executor):
114
125
  msg = 'cannot schedule futures after shutdown'
115
126
  raise RuntimeError(msg)
116
127
 
117
- self._work_queue.append(_WorkItem(f, fn, args, kwargs))
128
+ if sys.version_info >= (3, 14):
129
+ self._work_queue.append(_WorkItem(f, (fn, args, kwargs)))
130
+ else:
131
+ self._work_queue.append(_WorkItem(f, fn, args, kwargs))
118
132
 
119
133
  if _safe_call(self._idle.pop): # Pool is not maximized yet
120
134
  if q := _safe_call(_idle.pop): # Use idle worker
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes