pelutils 4.3.1__tar.gz → 4.5.0__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.
- pelutils-4.5.0/LICENSE.txt +19 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/PKG-INFO +55 -24
- {pelutils-4.3.1 → pelutils-4.5.0}/README.md +37 -15
- pelutils-4.5.0/pelutils/__init__.py +5 -0
- pelutils-4.5.0/pelutils/array/_unique.pyi +80 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/logging/_rich_string.py +3 -11
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/__init__.py +1 -1
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/_table.py +45 -15
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/serialization/_universal_json_model.py +15 -6
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/ticktock/__init__.py +2 -2
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/ticktock/_ticktock.py +67 -9
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils.egg-info/PKG-INFO +55 -24
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils.egg-info/SOURCES.txt +3 -1
- pelutils-4.5.0/pelutils.egg-info/requires.txt +10 -0
- pelutils-4.5.0/pyproject.toml +86 -0
- pelutils-4.5.0/setup.py +17 -0
- pelutils-4.3.1/pelutils/__init__.py +0 -3
- pelutils-4.3.1/pelutils/__version__.py +0 -2
- pelutils-4.3.1/pelutils.egg-info/requires.txt +0 -33
- pelutils-4.3.1/setup.py +0 -95
- {pelutils-4.3.1 → pelutils-4.5.0}/MANIFEST.in +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/array/blob.c +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/array/blob.h +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/array/unique.c +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/array/unique.h +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/deque/deque.c +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/deque/deque.h +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/hashmap/LICENSE +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/hashmap/hashmap.c +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/hashmap/hashmap.h +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/pelutils.c +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_c/types.h +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_entry_points/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/_entry_points/linecounter.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/array/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/array/_blob.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/array/_unique.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/array/_utils.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/job_parser/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/job_parser/_job_parser.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/job_parser/_structs.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/legacy/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/legacy/v3/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/legacy/v3/_datastorage.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/logging/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/logging/_logger.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/logging/_rotate.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/_conditional_import.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/_files.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/_git.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/_misc.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/misc/_platform.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/plots/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/plots/_figure.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/plots/_histogram.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/plots/_utils.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/py.typed +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/serialization/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/serialization/_jsonl.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/serialization/_pretty_json.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/stats/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/stats/_distributions.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/stats/_stats.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/tests/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/tests/_tests.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/types/__init__.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils/types/_types.py +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils.egg-info/dependency_links.txt +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils.egg-info/entry_points.txt +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/pelutils.egg-info/top_level.txt +0 -0
- {pelutils-4.3.1 → pelutils-4.5.0}/setup.cfg +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2022 Asger Laurits Schultz and Søren Winkel Holm
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pelutils
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.0
|
|
4
4
|
Summary: The Swiss army knife of Python projects
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
License: MIT
|
|
5
|
+
Author-email: Asger Laurits Schultz <asger.s@protonmail.com>, Søren Winkel Holm <swholm@protonmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/peleiden/pelutils
|
|
8
|
+
Project-URL: Download, https://pypi.org/project/pelutils/
|
|
10
9
|
Keywords: utility,logger,parser,profiling,plotting
|
|
11
10
|
Classifier: Intended Audience :: Developers
|
|
12
11
|
Classifier: Operating System :: OS Independent
|
|
@@ -20,9 +19,19 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.15
|
|
21
20
|
Requires-Python: >=3.11
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
|
-
Provides-Extra: docs
|
|
22
|
+
License-File: LICENSE.txt
|
|
25
23
|
License-File: pelutils/_c/hashmap/LICENSE
|
|
24
|
+
Requires-Dist: numpy>=2.0
|
|
25
|
+
Requires-Dist: gitpython>=3.1.0
|
|
26
|
+
Requires-Dist: rich>=10.0.0
|
|
27
|
+
Requires-Dist: py-cpuinfo>=8.0.0
|
|
28
|
+
Requires-Dist: psutil>=5.8.0
|
|
29
|
+
Requires-Dist: matplotlib>=3.3
|
|
30
|
+
Requires-Dist: scipy>=1.6
|
|
31
|
+
Requires-Dist: tqdm>=4.55
|
|
32
|
+
Requires-Dist: pydantic>=2
|
|
33
|
+
Requires-Dist: typing_extensions>=4.6
|
|
34
|
+
Dynamic: license-file
|
|
26
35
|
|
|
27
36
|
# pelutils
|
|
28
37
|
|
|
@@ -314,7 +323,7 @@ def process(features: FloatArray, labels: IntArray, mask: BoolArray): ...
|
|
|
314
323
|
|
|
315
324
|
## Also included
|
|
316
325
|
|
|
317
|
-
- `pelutils.misc.Table` — build aligned text tables which can
|
|
326
|
+
- `pelutils.misc.Table` — build aligned text tables which can be exported to LaTeX with `Table.to_latex()` or Markdown with `Table.to_markdown()`.
|
|
318
327
|
- `pelutils.misc.hardware_info` / `OS` — describe the machine the code runs on.
|
|
319
328
|
- `pelutils.misc.git_repo_info` — the repo and commit the code is executing in.
|
|
320
329
|
- Assorted file and dict helpers (`reverse_line_iterator`, `except_keys`, ...).
|
|
@@ -329,25 +338,47 @@ If no wheel matches, `pip` builds from source which requires `<Python.h>` — in
|
|
|
329
338
|
32-bit systems are not fully supported.
|
|
330
339
|
Most of the library is Python-only and should work but using any C-dependent code (namely `unique` and `SparseGridBlobDetection`) is likely to end in a segfault.
|
|
331
340
|
|
|
332
|
-
##
|
|
341
|
+
## Development
|
|
333
342
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
343
|
+
Use [uv](https://docs.astral.sh/uv/) for dependency management, builds, and running
|
|
344
|
+
development tools. Install uv, then from the repository root sync the development
|
|
345
|
+
environment (a C compiler and Python headers are required to build the native extension):
|
|
346
|
+
|
|
347
|
+
```sh
|
|
348
|
+
uv sync --python 3.11 --group dev
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The `dev` dependency group includes CPU-only PyTorch and documentation tools.
|
|
352
|
+
|
|
353
|
+
Run checks and tools through `uv run`:
|
|
339
354
|
|
|
340
|
-
When new code is merged into `master`, a number of checks are run.
|
|
341
|
-
These can be tested locally with the following commands.
|
|
342
355
|
```sh
|
|
343
356
|
# Linting and formatting
|
|
344
|
-
ruff format pelutils tests
|
|
345
|
-
ruff check pelutils tests
|
|
357
|
+
uv run --python 3.11 ruff format pelutils tests
|
|
358
|
+
uv run --python 3.11 ruff check pelutils tests
|
|
346
359
|
# Type checking
|
|
347
|
-
basedpyright pelutils
|
|
360
|
+
uv run --python 3.11 basedpyright pelutils
|
|
348
361
|
# Unit tests
|
|
349
|
-
python
|
|
362
|
+
uv run --python 3.11 pytest tests --cov pelutils
|
|
350
363
|
# Build docs
|
|
351
|
-
|
|
352
|
-
|
|
364
|
+
uv run --python 3.11 make -C docs html
|
|
365
|
+
# Preview at docs/build/html/index.html
|
|
353
366
|
```
|
|
367
|
+
|
|
368
|
+
After changing C sources, rebuild the extension with
|
|
369
|
+
`uv sync --python 3.11 --group dev --reinstall-package pelutils`.
|
|
370
|
+
|
|
371
|
+
The wheel-build CI tests each built wheel in a cibuildwheel environment provisioned
|
|
372
|
+
from the locked `dev` group with uv; pytest runs against the installed wheel. Read the
|
|
373
|
+
Docs also syncs that group through uv.
|
|
374
|
+
|
|
375
|
+
## Updating and releasing
|
|
376
|
+
|
|
377
|
+
`pelutils` uses the `master` branch as a stable development branch.
|
|
378
|
+
The `release` branch contains the latest version on PyPI.
|
|
379
|
+
New features should be made in feature branches from `master` that can be merged into `master` once ready.
|
|
380
|
+
When a new release is ready, update `pyproject.toml` and rebase `master` onto `release`.
|
|
381
|
+
Then push a new tag from `release` named `vX.Y.Z`.
|
|
382
|
+
|
|
383
|
+
When new code is merged into `master`, the checks listed above are run.
|
|
384
|
+
To build distribution artifacts locally, use `uv build --python 3.11`.
|
|
@@ -288,7 +288,7 @@ def process(features: FloatArray, labels: IntArray, mask: BoolArray): ...
|
|
|
288
288
|
|
|
289
289
|
## Also included
|
|
290
290
|
|
|
291
|
-
- `pelutils.misc.Table` — build aligned text tables which can
|
|
291
|
+
- `pelutils.misc.Table` — build aligned text tables which can be exported to LaTeX with `Table.to_latex()` or Markdown with `Table.to_markdown()`.
|
|
292
292
|
- `pelutils.misc.hardware_info` / `OS` — describe the machine the code runs on.
|
|
293
293
|
- `pelutils.misc.git_repo_info` — the repo and commit the code is executing in.
|
|
294
294
|
- Assorted file and dict helpers (`reverse_line_iterator`, `except_keys`, ...).
|
|
@@ -303,25 +303,47 @@ If no wheel matches, `pip` builds from source which requires `<Python.h>` — in
|
|
|
303
303
|
32-bit systems are not fully supported.
|
|
304
304
|
Most of the library is Python-only and should work but using any C-dependent code (namely `unique` and `SparseGridBlobDetection`) is likely to end in a segfault.
|
|
305
305
|
|
|
306
|
-
##
|
|
306
|
+
## Development
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
308
|
+
Use [uv](https://docs.astral.sh/uv/) for dependency management, builds, and running
|
|
309
|
+
development tools. Install uv, then from the repository root sync the development
|
|
310
|
+
environment (a C compiler and Python headers are required to build the native extension):
|
|
311
|
+
|
|
312
|
+
```sh
|
|
313
|
+
uv sync --python 3.11 --group dev
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The `dev` dependency group includes CPU-only PyTorch and documentation tools.
|
|
317
|
+
|
|
318
|
+
Run checks and tools through `uv run`:
|
|
313
319
|
|
|
314
|
-
When new code is merged into `master`, a number of checks are run.
|
|
315
|
-
These can be tested locally with the following commands.
|
|
316
320
|
```sh
|
|
317
321
|
# Linting and formatting
|
|
318
|
-
ruff format pelutils tests
|
|
319
|
-
ruff check pelutils tests
|
|
322
|
+
uv run --python 3.11 ruff format pelutils tests
|
|
323
|
+
uv run --python 3.11 ruff check pelutils tests
|
|
320
324
|
# Type checking
|
|
321
|
-
basedpyright pelutils
|
|
325
|
+
uv run --python 3.11 basedpyright pelutils
|
|
322
326
|
# Unit tests
|
|
323
|
-
python
|
|
327
|
+
uv run --python 3.11 pytest tests --cov pelutils
|
|
324
328
|
# Build docs
|
|
325
|
-
|
|
326
|
-
|
|
329
|
+
uv run --python 3.11 make -C docs html
|
|
330
|
+
# Preview at docs/build/html/index.html
|
|
327
331
|
```
|
|
332
|
+
|
|
333
|
+
After changing C sources, rebuild the extension with
|
|
334
|
+
`uv sync --python 3.11 --group dev --reinstall-package pelutils`.
|
|
335
|
+
|
|
336
|
+
The wheel-build CI tests each built wheel in a cibuildwheel environment provisioned
|
|
337
|
+
from the locked `dev` group with uv; pytest runs against the installed wheel. Read the
|
|
338
|
+
Docs also syncs that group through uv.
|
|
339
|
+
|
|
340
|
+
## Updating and releasing
|
|
341
|
+
|
|
342
|
+
`pelutils` uses the `master` branch as a stable development branch.
|
|
343
|
+
The `release` branch contains the latest version on PyPI.
|
|
344
|
+
New features should be made in feature branches from `master` that can be merged into `master` once ready.
|
|
345
|
+
When a new release is ready, update `pyproject.toml` and rebase `master` onto `release`.
|
|
346
|
+
Then push a new tag from `release` named `vX.Y.Z`.
|
|
347
|
+
|
|
348
|
+
When new code is merged into `master`, the checks listed above are run.
|
|
349
|
+
To build distribution artifacts locally, use `uv build --python 3.11`.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Literal, overload
|
|
4
|
+
|
|
5
|
+
import numpy.typing as npt
|
|
6
|
+
|
|
7
|
+
from pelutils.types import AnyArray, IntArray
|
|
8
|
+
|
|
9
|
+
@overload
|
|
10
|
+
def unique(
|
|
11
|
+
array: npt.ArrayLike,
|
|
12
|
+
*,
|
|
13
|
+
return_index: Literal[False] = False,
|
|
14
|
+
return_inverse: Literal[False] = False,
|
|
15
|
+
return_counts: Literal[False] = False,
|
|
16
|
+
axis: int = 0,
|
|
17
|
+
) -> AnyArray: ...
|
|
18
|
+
@overload
|
|
19
|
+
def unique(
|
|
20
|
+
array: npt.ArrayLike,
|
|
21
|
+
*,
|
|
22
|
+
return_index: Literal[True],
|
|
23
|
+
return_inverse: Literal[False] = False,
|
|
24
|
+
return_counts: Literal[False] = False,
|
|
25
|
+
axis: int = 0,
|
|
26
|
+
) -> tuple[AnyArray, IntArray]: ...
|
|
27
|
+
@overload
|
|
28
|
+
def unique(
|
|
29
|
+
array: npt.ArrayLike,
|
|
30
|
+
*,
|
|
31
|
+
return_index: Literal[False] = False,
|
|
32
|
+
return_inverse: Literal[True],
|
|
33
|
+
return_counts: Literal[False] = False,
|
|
34
|
+
axis: int = 0,
|
|
35
|
+
) -> tuple[AnyArray, IntArray]: ...
|
|
36
|
+
@overload
|
|
37
|
+
def unique(
|
|
38
|
+
array: npt.ArrayLike,
|
|
39
|
+
*,
|
|
40
|
+
return_index: Literal[True],
|
|
41
|
+
return_inverse: Literal[True],
|
|
42
|
+
return_counts: Literal[False] = False,
|
|
43
|
+
axis: int = 0,
|
|
44
|
+
) -> tuple[AnyArray, IntArray, IntArray]: ...
|
|
45
|
+
@overload
|
|
46
|
+
def unique(
|
|
47
|
+
array: npt.ArrayLike,
|
|
48
|
+
*,
|
|
49
|
+
return_index: Literal[False] = False,
|
|
50
|
+
return_inverse: Literal[False] = False,
|
|
51
|
+
return_counts: Literal[True],
|
|
52
|
+
axis: int = 0,
|
|
53
|
+
) -> tuple[AnyArray, IntArray]: ...
|
|
54
|
+
@overload
|
|
55
|
+
def unique(
|
|
56
|
+
array: npt.ArrayLike,
|
|
57
|
+
*,
|
|
58
|
+
return_index: Literal[True],
|
|
59
|
+
return_inverse: Literal[False] = False,
|
|
60
|
+
return_counts: Literal[True],
|
|
61
|
+
axis: int = 0,
|
|
62
|
+
) -> tuple[AnyArray, IntArray, IntArray]: ...
|
|
63
|
+
@overload
|
|
64
|
+
def unique(
|
|
65
|
+
array: npt.ArrayLike,
|
|
66
|
+
*,
|
|
67
|
+
return_index: Literal[False] = False,
|
|
68
|
+
return_inverse: Literal[True],
|
|
69
|
+
return_counts: Literal[True],
|
|
70
|
+
axis: int = 0,
|
|
71
|
+
) -> tuple[AnyArray, IntArray, IntArray]: ...
|
|
72
|
+
@overload
|
|
73
|
+
def unique(
|
|
74
|
+
array: npt.ArrayLike,
|
|
75
|
+
*,
|
|
76
|
+
return_index: Literal[True],
|
|
77
|
+
return_inverse: Literal[True],
|
|
78
|
+
return_counts: Literal[True],
|
|
79
|
+
axis: int = 0,
|
|
80
|
+
) -> tuple[AnyArray, IntArray, IntArray, IntArray]: ...
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import re
|
|
2
|
-
|
|
3
|
-
from rich.color import ANSI_COLOR_NAMES
|
|
4
1
|
from rich.console import Console
|
|
2
|
+
from rich.markup import escape
|
|
5
3
|
from typing_extensions import override
|
|
6
4
|
|
|
7
5
|
_stdout_console = Console(highlight=False)
|
|
@@ -14,10 +12,6 @@ class RichString:
|
|
|
14
12
|
This allows for printing and logging without rich syntax causing issues.
|
|
15
13
|
"""
|
|
16
14
|
|
|
17
|
-
_open_tag_regex = re.compile("(%s)" % "|".join(r"\[" + c + r"\]" for c in ANSI_COLOR_NAMES)) # noqa: UP031
|
|
18
|
-
_close_tag_regex_1 = re.compile(r"\\(\[\/.*\])")
|
|
19
|
-
_close_tag_regex_2 = re.compile(r"(\[\/.*\])")
|
|
20
|
-
|
|
21
15
|
def __init__(self, stderr: bool = False):
|
|
22
16
|
self.strings: list[str] = list() # Normal strings
|
|
23
17
|
self.riches: list[str] = list() # Corresponding strings with rich syntax
|
|
@@ -26,10 +20,8 @@ class RichString:
|
|
|
26
20
|
def add_string(self, s: str, rich: str | None = None):
|
|
27
21
|
"""Add a new string and optionally a rich string equivalent."""
|
|
28
22
|
if rich is None:
|
|
29
|
-
# Escape
|
|
30
|
-
rich =
|
|
31
|
-
rich = re.sub(self._close_tag_regex_1, r"\1", rich)
|
|
32
|
-
rich = re.sub(self._close_tag_regex_2, r"\\\1", rich)
|
|
23
|
+
# Escape all markup so plain text cannot be mistaken for Rich formatting.
|
|
24
|
+
rich = escape(s)
|
|
33
25
|
self.strings.append(s)
|
|
34
26
|
self.riches.append(rich)
|
|
35
27
|
|
|
@@ -6,7 +6,7 @@ and rewriting them each time is tedious and error-prone. This module is the grab
|
|
|
6
6
|
those: each helper is self-contained and useful on its own, so import only what you need.
|
|
7
7
|
|
|
8
8
|
Main inclusions are :class:`Table` for aligned text tables that also export to LaTeX via
|
|
9
|
-
:meth:`Table.to_latex`; :class:`OS` and :data:`hardware_info` for describing the machine
|
|
9
|
+
:meth:`Table.to_latex` and Markdown via :meth:`Table.to_markdown`; :class:`OS` and :data:`hardware_info` for describing the machine
|
|
10
10
|
the code runs on; :func:`git_repo_info` for the repository and commit currently executing;
|
|
11
11
|
:func:`array_bytes`/:func:`array_ptr` for low-level array introspection; and small file
|
|
12
12
|
and dict helpers such as :func:`reverse_line_iterator` and :func:`except_keys`.
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import re
|
|
2
1
|
from collections.abc import Sequence
|
|
3
2
|
from typing import Any
|
|
4
3
|
|
|
4
|
+
from rich.cells import cell_len
|
|
5
5
|
from typing_extensions import override
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Table:
|
|
9
|
-
"""Build aligned, human-readable text tables that can also be exported to LaTeX.
|
|
9
|
+
"""Build aligned, human-readable text tables that can also be exported to LaTeX and Markdown.
|
|
10
10
|
|
|
11
11
|
Add an optional header, then rows, and render with ``str(table)``; column widths are
|
|
12
12
|
computed automatically. :meth:`add_hline` inserts a horizontal rule and
|
|
13
|
-
:meth:`to_latex` produces ``booktabs``-style LaTeX
|
|
13
|
+
:meth:`to_latex` produces ``booktabs``-style LaTeX, and :meth:`to_markdown`
|
|
14
|
+
produces a Markdown pipe table.
|
|
14
15
|
|
|
15
16
|
Example
|
|
16
17
|
-------
|
|
@@ -67,29 +68,58 @@ class Table:
|
|
|
67
68
|
self._hlines.add(len(self._rows) - 1)
|
|
68
69
|
|
|
69
70
|
def to_latex(self) -> str:
|
|
70
|
-
"""Produce LaTeX code for the table to
|
|
71
|
+
"""Produce LaTeX code for the table to include in a tabular environment.
|
|
71
72
|
|
|
72
73
|
It assumes the booktabs package is used.
|
|
73
74
|
"""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
assert self._width is not None
|
|
76
|
+
all_rows = [self._header, *self._rows] if self._header else self._rows
|
|
77
|
+
widths = [max(cell_len(row[j]) for row in all_rows) for j in range(self._width)]
|
|
78
|
+
|
|
79
|
+
lines = [r"\toprule"]
|
|
80
|
+
if self._header:
|
|
81
|
+
header = (self._format_element(cell, width, True) for cell, width in zip(self._header, widths, strict=True))
|
|
82
|
+
lines.append(" & ".join(header) + r" \\")
|
|
83
|
+
lines.append(r"\midrule")
|
|
84
|
+
|
|
85
|
+
for i, (row, left_align) in enumerate(zip(self._rows, self._left_aligns, strict=True)):
|
|
86
|
+
cells = (self._format_element(cell, width, align) for cell, width, align in zip(row, widths, left_align, strict=True))
|
|
87
|
+
lines.append(" & ".join(cells) + r" \\")
|
|
88
|
+
if i in self._hlines:
|
|
89
|
+
lines.append(r"\midrule")
|
|
90
|
+
|
|
77
91
|
lines.append(r"\bottomrule")
|
|
92
|
+
return "\n".join(lines)
|
|
78
93
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
lines[i] = r"\midrule"
|
|
82
|
-
elif re.match(r"^(.+\|)+.+$", line):
|
|
83
|
-
lines[i] = line.replace("|", "&") + r" \\"
|
|
94
|
+
def to_markdown(self) -> str:
|
|
95
|
+
"""Produce a Markdown pipe table.
|
|
84
96
|
|
|
97
|
+
Markdown requires a header row, so a table without an explicit header gets a
|
|
98
|
+
blank one. Column alignment follows the first data row (or is left-aligned
|
|
99
|
+
when there are no data rows). Markdown has no syntax for horizontal rules
|
|
100
|
+
between body rows, so markers added with :meth:`add_hline` are omitted.
|
|
101
|
+
"""
|
|
102
|
+
assert self._width is not None
|
|
103
|
+
header = self._header or ("",) * self._width
|
|
104
|
+
aligns = self._left_aligns[0] if self._left_aligns else (True,) * self._width
|
|
105
|
+
lines = [
|
|
106
|
+
"| " + " | ".join(self._escape_markdown_cell(cell) for cell in header) + " |",
|
|
107
|
+
"| " + " | ".join(":---" if align else "---:" for align in aligns) + " |",
|
|
108
|
+
]
|
|
109
|
+
lines.extend("| " + " | ".join(self._escape_markdown_cell(cell) for cell in row) + " |" for row in self._rows)
|
|
85
110
|
return "\n".join(lines)
|
|
86
111
|
|
|
112
|
+
@staticmethod
|
|
113
|
+
def _escape_markdown_cell(cell: str) -> str:
|
|
114
|
+
return cell.replace("\\", "\\\\").replace("|", r"\|").replace("\r\n", "\n").replace("\r", "\n").replace("\n", "<br>")
|
|
115
|
+
|
|
87
116
|
@staticmethod
|
|
88
117
|
def _format_element(element: str, width: int, left_align: bool) -> str:
|
|
118
|
+
padding = width - cell_len(element)
|
|
89
119
|
if left_align:
|
|
90
|
-
return element + " " *
|
|
120
|
+
return element + " " * padding
|
|
91
121
|
else:
|
|
92
|
-
return " " *
|
|
122
|
+
return " " * padding + element
|
|
93
123
|
|
|
94
124
|
@override
|
|
95
125
|
def __repr__(self) -> str:
|
|
@@ -99,7 +129,7 @@ class Table:
|
|
|
99
129
|
def __str__(self) -> str:
|
|
100
130
|
assert self._width is not None
|
|
101
131
|
all_rows = [self._header, *self._rows] if self._header else self._rows
|
|
102
|
-
widths = [max(
|
|
132
|
+
widths = [max(cell_len(all_rows[i][j]) for i in range(len(all_rows))) for j in range(self._width)]
|
|
103
133
|
hline = "+".join("-" * (width + 1 + (0 < i < self._width - 1)) for i, width in enumerate(widths))
|
|
104
134
|
strs = list()
|
|
105
135
|
if self._header:
|
|
@@ -22,15 +22,24 @@ class UniversalJsonModel(BaseModel):
|
|
|
22
22
|
|
|
23
23
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
24
24
|
|
|
25
|
-
def to_json_dict(self) -> dict[str, Any]: # pyright: ignore[reportExplicitAny]
|
|
26
|
-
"""Return a JSON-compatible dictionary, pickle-encoding unsupported values.
|
|
27
|
-
|
|
25
|
+
def to_json_dict(self, **model_dump_kwargs: Any) -> dict[str, Any]: # pyright: ignore[reportExplicitAny]
|
|
26
|
+
"""Return a JSON-compatible dictionary, pickle-encoding unsupported values.
|
|
27
|
+
|
|
28
|
+
Keyword arguments are forwarded to :meth:`pydantic.BaseModel.model_dump`, except
|
|
29
|
+
``mode`` and ``fallback``, which are fixed to preserve this method's JSON format.
|
|
30
|
+
"""
|
|
31
|
+
if {"mode", "fallback"} & model_dump_kwargs.keys():
|
|
32
|
+
raise TypeError("'mode' and 'fallback' are fixed by to_json_dict()")
|
|
33
|
+
return self.model_dump(mode="json", fallback=pickle_encode, **model_dump_kwargs)
|
|
28
34
|
|
|
29
35
|
@classmethod
|
|
30
|
-
def from_json_dict(cls: type[Self], json_dict: dict[str, Any]) -> Self: # pyright: ignore[reportExplicitAny]
|
|
31
|
-
"""Build a model from :meth:`to_json_dict` output. Do not load untrusted data.
|
|
36
|
+
def from_json_dict(cls: type[Self], json_dict: dict[str, Any], **model_validate_kwargs: Any) -> Self: # pyright: ignore[reportExplicitAny]
|
|
37
|
+
"""Build a model from :meth:`to_json_dict` output. Do not load untrusted data.
|
|
38
|
+
|
|
39
|
+
Keyword arguments are forwarded to :meth:`pydantic.BaseModel.model_validate`.
|
|
40
|
+
"""
|
|
32
41
|
self_dict = from_safe_json(json_dict)
|
|
33
|
-
return cls.model_validate(self_dict)
|
|
42
|
+
return cls.model_validate(self_dict, **model_validate_kwargs)
|
|
34
43
|
|
|
35
44
|
def save(self, path: str | Path, *, max_line_length: int = 140, indent: int = 2, encoding: str = "utf-8"):
|
|
36
45
|
"""Save the model to ``path`` as human-readable JSON, creating parent directories as needed."""
|
|
@@ -37,6 +37,6 @@ periodic tasks, and ``fuse``/``fuse_multiple`` for combining results across
|
|
|
37
37
|
processes.
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
|
-
from ._ticktock import TT, Profile, TickTock, TickTockException
|
|
40
|
+
from ._ticktock import TT, Profile, TickTock, TickTockException, get_active_ticktock
|
|
41
41
|
|
|
42
|
-
__all__ = ("TT", "Profile", "TickTock", "TickTockException")
|
|
42
|
+
__all__ = ("TT", "Profile", "TickTock", "TickTockException", "get_active_ticktock")
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import threading
|
|
1
3
|
import warnings
|
|
2
4
|
from collections.abc import Hashable, Iterable, Iterator, Sequence
|
|
3
5
|
from contextlib import contextmanager
|
|
4
6
|
from copy import deepcopy
|
|
5
|
-
from threading import current_thread
|
|
6
7
|
from time import perf_counter
|
|
7
8
|
from typing import TypeAlias, TypeVar, cast
|
|
8
9
|
|
|
@@ -23,6 +24,20 @@ _time_units: tuple[_TimeUnit, ...] = (
|
|
|
23
24
|
|
|
24
25
|
T = TypeVar("T")
|
|
25
26
|
|
|
27
|
+
_lock = threading.RLock()
|
|
28
|
+
_active_ticktocks: "dict[int, TickTock]" = dict()
|
|
29
|
+
_ids = itertools.count()
|
|
30
|
+
_ids_lock = threading.RLock()
|
|
31
|
+
_local_thread = threading.local()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_thread_uid() -> int:
|
|
35
|
+
"""Return a stable, program-lifetime-unique ID for the calling thread."""
|
|
36
|
+
if not hasattr(_local_thread, "__ticktock_uid__"):
|
|
37
|
+
with _ids_lock:
|
|
38
|
+
_local_thread.__ticktock_uid__ = next(_ids)
|
|
39
|
+
return _local_thread.__ticktock_uid__
|
|
40
|
+
|
|
26
41
|
|
|
27
42
|
def _get_smallest_suitable_unit(duration_s: float) -> _TimeUnit:
|
|
28
43
|
"""Return the smallest unit for which the duration is at least the same size as the unit."""
|
|
@@ -160,8 +175,8 @@ class TickTock:
|
|
|
160
175
|
self._profile_stack: list[Profile] = list() # LIFO stack of active profiles
|
|
161
176
|
self._root_profiles: list[Profile] = list() # Top level profiles
|
|
162
177
|
|
|
163
|
-
self._thread_name = current_thread().name
|
|
164
|
-
self._thread_id =
|
|
178
|
+
self._thread_name = threading.current_thread().name
|
|
179
|
+
self._thread_id = _get_thread_uid()
|
|
165
180
|
|
|
166
181
|
def tick(self, key: Hashable = None):
|
|
167
182
|
"""Start a timer identified by an optional hashable key."""
|
|
@@ -244,10 +259,10 @@ class TickTock:
|
|
|
244
259
|
self._end_profile()
|
|
245
260
|
|
|
246
261
|
def _warn_if_wrong_thread(self, *, stacklevel: int):
|
|
247
|
-
if self._thread_id !=
|
|
262
|
+
if self._thread_id != _get_thread_uid():
|
|
248
263
|
warnings.warn(
|
|
249
264
|
f"This TickTock instance was created in the {self._thread_name} thread but profiling was started in "
|
|
250
|
-
+ f"{current_thread().name}. Profiling is NOT designed to deal with multiple threads. Instead, create a "
|
|
265
|
+
+ f"{threading.current_thread().name}. Profiling is NOT designed to deal with multiple threads. Instead, create a "
|
|
251
266
|
+ "TickTock instance for each thread requiring profiling.",
|
|
252
267
|
stacklevel=stacklevel,
|
|
253
268
|
)
|
|
@@ -332,13 +347,23 @@ class TickTock:
|
|
|
332
347
|
profile.parent.children.remove(profile)
|
|
333
348
|
|
|
334
349
|
def reset(self):
|
|
335
|
-
"""Stop all timing and profiling and clear all profiles and measurements.
|
|
350
|
+
"""Stop all timing and profiling and clear all profiles and measurements.
|
|
351
|
+
|
|
352
|
+
Resetting is allowed while this instance is registered by :meth:`as_active`; the
|
|
353
|
+
active registration remains until that context exits. Resetting while a profile
|
|
354
|
+
context is in progress raises :class:`TickTockException` without changing state.
|
|
355
|
+
"""
|
|
336
356
|
if self._profile_stack:
|
|
337
357
|
raise TickTockException("Cannot reset TickTock while profiling is active")
|
|
338
358
|
self.__init__()
|
|
339
359
|
|
|
340
360
|
def reset_profiles(self):
|
|
341
|
-
"""
|
|
361
|
+
"""Reset profiles while leaving ``tick``/``tock`` timers intact.
|
|
362
|
+
|
|
363
|
+
The same restrictions as :meth:`reset` apply: this is allowed while registered
|
|
364
|
+
by :meth:`as_active`, but raises :class:`TickTockException` during a profile
|
|
365
|
+
context.
|
|
366
|
+
"""
|
|
342
367
|
tick_starts = self._tick_starts
|
|
343
368
|
self.reset()
|
|
344
369
|
self._tick_starts = tick_starts
|
|
@@ -393,8 +418,8 @@ class TickTock:
|
|
|
393
418
|
raise ValueError("Some TickTocks are the same instance, which is not allowed")
|
|
394
419
|
for tt in tts[1:]:
|
|
395
420
|
ticktock.fuse(tt)
|
|
396
|
-
ticktock._thread_name = current_thread().name
|
|
397
|
-
ticktock._thread_id =
|
|
421
|
+
ticktock._thread_name = threading.current_thread().name
|
|
422
|
+
ticktock._thread_id = _get_thread_uid()
|
|
398
423
|
return ticktock
|
|
399
424
|
|
|
400
425
|
@staticmethod
|
|
@@ -446,5 +471,38 @@ class TickTock:
|
|
|
446
471
|
"""Return True if any profiling has been performed."""
|
|
447
472
|
return len(self._root_profiles) > 0
|
|
448
473
|
|
|
474
|
+
@contextmanager
|
|
475
|
+
def as_active(self):
|
|
476
|
+
"""Set the ``TickTock`` instance as the active instance in the current thread.
|
|
477
|
+
|
|
478
|
+
It can then be retrieved with :func:`get_active_ticktock`.
|
|
479
|
+
Calling :meth:`reset` or :meth:`reset_profiles` inside this context does not
|
|
480
|
+
unset the active instance. Those methods still raise if a profile context is
|
|
481
|
+
currently in progress.
|
|
482
|
+
"""
|
|
483
|
+
current_thread_id = _get_thread_uid()
|
|
484
|
+
with _lock:
|
|
485
|
+
if current_thread_id in _active_ticktocks:
|
|
486
|
+
raise TickTockException(f"An active TickTock instance has already been set in thread {threading.current_thread().name}")
|
|
487
|
+
_active_ticktocks[current_thread_id] = self
|
|
488
|
+
try:
|
|
489
|
+
yield
|
|
490
|
+
finally:
|
|
491
|
+
with _lock:
|
|
492
|
+
del _active_ticktocks[current_thread_id]
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def get_active_ticktock() -> TickTock:
|
|
496
|
+
"""Return the active ``TickTock`` instance in the current thread (as set by :meth:`TickTock.as_active`).
|
|
497
|
+
|
|
498
|
+
If no active ``TickTock`` is set, a new ``TickTock`` instance is returned.
|
|
499
|
+
This allows normal profiling usage without having to care if it's actually used or not.
|
|
500
|
+
"""
|
|
501
|
+
with _lock:
|
|
502
|
+
ticktock = _active_ticktocks.get(_get_thread_uid())
|
|
503
|
+
return ticktock if ticktock is not None else TickTock()
|
|
504
|
+
|
|
449
505
|
|
|
506
|
+
# Globally accessible default instance
|
|
507
|
+
# It suffices to use this for most cases
|
|
450
508
|
TT = TickTock()
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pelutils
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.5.0
|
|
4
4
|
Summary: The Swiss army knife of Python projects
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
License: MIT
|
|
5
|
+
Author-email: Asger Laurits Schultz <asger.s@protonmail.com>, Søren Winkel Holm <swholm@protonmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/peleiden/pelutils
|
|
8
|
+
Project-URL: Download, https://pypi.org/project/pelutils/
|
|
10
9
|
Keywords: utility,logger,parser,profiling,plotting
|
|
11
10
|
Classifier: Intended Audience :: Developers
|
|
12
11
|
Classifier: Operating System :: OS Independent
|
|
@@ -20,9 +19,19 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.15
|
|
21
20
|
Requires-Python: >=3.11
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
|
-
|
|
24
|
-
Provides-Extra: docs
|
|
22
|
+
License-File: LICENSE.txt
|
|
25
23
|
License-File: pelutils/_c/hashmap/LICENSE
|
|
24
|
+
Requires-Dist: numpy>=2.0
|
|
25
|
+
Requires-Dist: gitpython>=3.1.0
|
|
26
|
+
Requires-Dist: rich>=10.0.0
|
|
27
|
+
Requires-Dist: py-cpuinfo>=8.0.0
|
|
28
|
+
Requires-Dist: psutil>=5.8.0
|
|
29
|
+
Requires-Dist: matplotlib>=3.3
|
|
30
|
+
Requires-Dist: scipy>=1.6
|
|
31
|
+
Requires-Dist: tqdm>=4.55
|
|
32
|
+
Requires-Dist: pydantic>=2
|
|
33
|
+
Requires-Dist: typing_extensions>=4.6
|
|
34
|
+
Dynamic: license-file
|
|
26
35
|
|
|
27
36
|
# pelutils
|
|
28
37
|
|
|
@@ -314,7 +323,7 @@ def process(features: FloatArray, labels: IntArray, mask: BoolArray): ...
|
|
|
314
323
|
|
|
315
324
|
## Also included
|
|
316
325
|
|
|
317
|
-
- `pelutils.misc.Table` — build aligned text tables which can
|
|
326
|
+
- `pelutils.misc.Table` — build aligned text tables which can be exported to LaTeX with `Table.to_latex()` or Markdown with `Table.to_markdown()`.
|
|
318
327
|
- `pelutils.misc.hardware_info` / `OS` — describe the machine the code runs on.
|
|
319
328
|
- `pelutils.misc.git_repo_info` — the repo and commit the code is executing in.
|
|
320
329
|
- Assorted file and dict helpers (`reverse_line_iterator`, `except_keys`, ...).
|
|
@@ -329,25 +338,47 @@ If no wheel matches, `pip` builds from source which requires `<Python.h>` — in
|
|
|
329
338
|
32-bit systems are not fully supported.
|
|
330
339
|
Most of the library is Python-only and should work but using any C-dependent code (namely `unique` and `SparseGridBlobDetection`) is likely to end in a segfault.
|
|
331
340
|
|
|
332
|
-
##
|
|
341
|
+
## Development
|
|
333
342
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
343
|
+
Use [uv](https://docs.astral.sh/uv/) for dependency management, builds, and running
|
|
344
|
+
development tools. Install uv, then from the repository root sync the development
|
|
345
|
+
environment (a C compiler and Python headers are required to build the native extension):
|
|
346
|
+
|
|
347
|
+
```sh
|
|
348
|
+
uv sync --python 3.11 --group dev
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The `dev` dependency group includes CPU-only PyTorch and documentation tools.
|
|
352
|
+
|
|
353
|
+
Run checks and tools through `uv run`:
|
|
339
354
|
|
|
340
|
-
When new code is merged into `master`, a number of checks are run.
|
|
341
|
-
These can be tested locally with the following commands.
|
|
342
355
|
```sh
|
|
343
356
|
# Linting and formatting
|
|
344
|
-
ruff format pelutils tests
|
|
345
|
-
ruff check pelutils tests
|
|
357
|
+
uv run --python 3.11 ruff format pelutils tests
|
|
358
|
+
uv run --python 3.11 ruff check pelutils tests
|
|
346
359
|
# Type checking
|
|
347
|
-
basedpyright pelutils
|
|
360
|
+
uv run --python 3.11 basedpyright pelutils
|
|
348
361
|
# Unit tests
|
|
349
|
-
python
|
|
362
|
+
uv run --python 3.11 pytest tests --cov pelutils
|
|
350
363
|
# Build docs
|
|
351
|
-
|
|
352
|
-
|
|
364
|
+
uv run --python 3.11 make -C docs html
|
|
365
|
+
# Preview at docs/build/html/index.html
|
|
353
366
|
```
|
|
367
|
+
|
|
368
|
+
After changing C sources, rebuild the extension with
|
|
369
|
+
`uv sync --python 3.11 --group dev --reinstall-package pelutils`.
|
|
370
|
+
|
|
371
|
+
The wheel-build CI tests each built wheel in a cibuildwheel environment provisioned
|
|
372
|
+
from the locked `dev` group with uv; pytest runs against the installed wheel. Read the
|
|
373
|
+
Docs also syncs that group through uv.
|
|
374
|
+
|
|
375
|
+
## Updating and releasing
|
|
376
|
+
|
|
377
|
+
`pelutils` uses the `master` branch as a stable development branch.
|
|
378
|
+
The `release` branch contains the latest version on PyPI.
|
|
379
|
+
New features should be made in feature branches from `master` that can be merged into `master` once ready.
|
|
380
|
+
When a new release is ready, update `pyproject.toml` and rebase `master` onto `release`.
|
|
381
|
+
Then push a new tag from `release` named `vX.Y.Z`.
|
|
382
|
+
|
|
383
|
+
When new code is merged into `master`, the checks listed above are run.
|
|
384
|
+
To build distribution artifacts locally, use `uv build --python 3.11`.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
LICENSE.txt
|
|
1
2
|
MANIFEST.in
|
|
2
3
|
README.md
|
|
4
|
+
pyproject.toml
|
|
3
5
|
setup.py
|
|
4
6
|
pelutils/__init__.py
|
|
5
|
-
pelutils/__version__.py
|
|
6
7
|
pelutils/py.typed
|
|
7
8
|
pelutils.egg-info/PKG-INFO
|
|
8
9
|
pelutils.egg-info/SOURCES.txt
|
|
@@ -27,6 +28,7 @@ pelutils/_entry_points/linecounter.py
|
|
|
27
28
|
pelutils/array/__init__.py
|
|
28
29
|
pelutils/array/_blob.py
|
|
29
30
|
pelutils/array/_unique.py
|
|
31
|
+
pelutils/array/_unique.pyi
|
|
30
32
|
pelutils/array/_utils.py
|
|
31
33
|
pelutils/job_parser/__init__.py
|
|
32
34
|
pelutils/job_parser/_job_parser.py
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# uv_build does not support the native C extension; uv uses this PEP 517 backend.
|
|
2
|
+
[build-system]
|
|
3
|
+
requires = ["setuptools>=77", "wheel"]
|
|
4
|
+
build-backend = "setuptools.build_meta"
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "pelutils"
|
|
8
|
+
version = "4.5.0"
|
|
9
|
+
description = "The Swiss army knife of Python projects"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
license-files = ["LICENSE.txt", "pelutils/_c/hashmap/LICENSE"]
|
|
14
|
+
authors = [
|
|
15
|
+
{ name = "Asger Laurits Schultz", email = "asger.s@protonmail.com" },
|
|
16
|
+
{ name = "Søren Winkel Holm", email = "swholm@protonmail.com" },
|
|
17
|
+
]
|
|
18
|
+
keywords = ["utility", "logger", "parser", "profiling", "plotting"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: Free Threading :: 3 - Stable",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Programming Language :: Python :: 3.15",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"numpy>=2.0",
|
|
33
|
+
"gitpython>=3.1.0",
|
|
34
|
+
"rich>=10.0.0",
|
|
35
|
+
"py-cpuinfo>=8.0.0",
|
|
36
|
+
"psutil>=5.8.0",
|
|
37
|
+
"matplotlib>=3.3",
|
|
38
|
+
"scipy>=1.6",
|
|
39
|
+
"tqdm>=4.55",
|
|
40
|
+
"pydantic>=2",
|
|
41
|
+
"typing_extensions>=4.6",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[dependency-groups]
|
|
45
|
+
dev = [
|
|
46
|
+
"torch>=2",
|
|
47
|
+
"pytest==8.4.2",
|
|
48
|
+
"pytest-cov==7.0.0",
|
|
49
|
+
"coveralls>=4.0.0",
|
|
50
|
+
"coverage==7.10.7",
|
|
51
|
+
"wheel",
|
|
52
|
+
"setuptools>=60.0.0",
|
|
53
|
+
"ruff==0.15.8",
|
|
54
|
+
"basedpyright==1.39.0",
|
|
55
|
+
"freezegun>=1.5",
|
|
56
|
+
"ipdb",
|
|
57
|
+
"sphinx",
|
|
58
|
+
"sphinx-autobuild",
|
|
59
|
+
"furo",
|
|
60
|
+
"myst-parser",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[project.scripts]
|
|
64
|
+
linecounter = "pelutils._entry_points.linecounter:run"
|
|
65
|
+
|
|
66
|
+
[project.urls]
|
|
67
|
+
Homepage = "https://github.com/peleiden/pelutils"
|
|
68
|
+
Download = "https://pypi.org/project/pelutils/"
|
|
69
|
+
|
|
70
|
+
[tool.setuptools]
|
|
71
|
+
include-package-data = true
|
|
72
|
+
|
|
73
|
+
[tool.setuptools.packages.find]
|
|
74
|
+
exclude = ["tests", "tests.*"]
|
|
75
|
+
namespaces = false
|
|
76
|
+
|
|
77
|
+
[tool.setuptools.package-data]
|
|
78
|
+
pelutils = ["py.typed"]
|
|
79
|
+
|
|
80
|
+
[[tool.uv.index]]
|
|
81
|
+
name = "pytorch"
|
|
82
|
+
url = "https://download.pytorch.org/whl/cpu"
|
|
83
|
+
explicit = true
|
|
84
|
+
|
|
85
|
+
[tool.uv.sources]
|
|
86
|
+
torch = { index = "pytorch" }
|
pelutils-4.5.0/setup.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from setuptools import setup
|
|
5
|
+
from setuptools.extension import Extension
|
|
6
|
+
|
|
7
|
+
c_files = [os.path.join(root, filename) for root, _, files in os.walk("pelutils/_c") for filename in files if filename.endswith(".c")]
|
|
8
|
+
|
|
9
|
+
setup(
|
|
10
|
+
ext_modules=[
|
|
11
|
+
Extension(
|
|
12
|
+
name="_pelutils_c",
|
|
13
|
+
sources=c_files,
|
|
14
|
+
extra_compile_args=["-DMS_WIN64"] if sys.platform == "win32" else [],
|
|
15
|
+
)
|
|
16
|
+
]
|
|
17
|
+
)
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
gitpython>=3.1.0
|
|
2
|
-
matplotlib>=3.3
|
|
3
|
-
numpy>=2.0
|
|
4
|
-
psutil>=5.8.0
|
|
5
|
-
py-cpuinfo>=8.0.0
|
|
6
|
-
pydantic>=2
|
|
7
|
-
rich>=10.0.0
|
|
8
|
-
scipy>=1.6
|
|
9
|
-
tqdm>=4.55
|
|
10
|
-
typing_extensions>=4.6
|
|
11
|
-
|
|
12
|
-
[dev]
|
|
13
|
-
basedpyright==1.39.0
|
|
14
|
-
coverage==7.10.7
|
|
15
|
-
coveralls>=4.0.0
|
|
16
|
-
freezegun>=1.5
|
|
17
|
-
furo
|
|
18
|
-
ipdb
|
|
19
|
-
myst-parser
|
|
20
|
-
pytest-cov==7.0.0
|
|
21
|
-
pytest==8.4.2
|
|
22
|
-
ruff==0.15.8
|
|
23
|
-
setuptools>=60.0.0
|
|
24
|
-
sphinx
|
|
25
|
-
sphinx-autobuild
|
|
26
|
-
torch>=2
|
|
27
|
-
wheel
|
|
28
|
-
|
|
29
|
-
[docs]
|
|
30
|
-
furo
|
|
31
|
-
myst-parser
|
|
32
|
-
sphinx
|
|
33
|
-
sphinx-autobuild
|
pelutils-4.3.1/setup.py
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
from glob import glob as glob # glob
|
|
4
|
-
|
|
5
|
-
from setuptools import find_packages, setup
|
|
6
|
-
from setuptools.extension import Extension
|
|
7
|
-
|
|
8
|
-
sys.path.append("pelutils")
|
|
9
|
-
from __version__ import __version__
|
|
10
|
-
|
|
11
|
-
requirements = [
|
|
12
|
-
"numpy>=2.0",
|
|
13
|
-
"gitpython>=3.1.0",
|
|
14
|
-
"rich>=10.0.0",
|
|
15
|
-
"py-cpuinfo>=8.0.0",
|
|
16
|
-
"psutil>=5.8.0",
|
|
17
|
-
"matplotlib>=3.3",
|
|
18
|
-
"scipy>=1.6",
|
|
19
|
-
"tqdm>=4.55",
|
|
20
|
-
"pydantic>=2",
|
|
21
|
-
"typing_extensions>=4.6",
|
|
22
|
-
]
|
|
23
|
-
requirements_dev = [
|
|
24
|
-
"torch>=2",
|
|
25
|
-
"pytest==8.4.2",
|
|
26
|
-
"pytest-cov==7.0.0",
|
|
27
|
-
"coveralls>=4.0.0",
|
|
28
|
-
"coverage==7.10.7",
|
|
29
|
-
"wheel",
|
|
30
|
-
"setuptools>=60.0.0",
|
|
31
|
-
"ruff==0.15.8",
|
|
32
|
-
"basedpyright==1.39.0",
|
|
33
|
-
"freezegun>=1.5",
|
|
34
|
-
"ipdb",
|
|
35
|
-
]
|
|
36
|
-
requirements_docs = [
|
|
37
|
-
"sphinx",
|
|
38
|
-
"sphinx-autobuild",
|
|
39
|
-
"furo",
|
|
40
|
-
"myst-parser",
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
with open("README.md") as readme_file:
|
|
44
|
-
README = readme_file.read()
|
|
45
|
-
|
|
46
|
-
c_files = list()
|
|
47
|
-
for root, __, files in os.walk("pelutils/_c"):
|
|
48
|
-
c_files += [os.path.join(root, f) for f in files if f.endswith(".c")]
|
|
49
|
-
|
|
50
|
-
setup_args = dict(
|
|
51
|
-
name="pelutils",
|
|
52
|
-
version=__version__,
|
|
53
|
-
description="The Swiss army knife of Python projects",
|
|
54
|
-
long_description_content_type="text/markdown",
|
|
55
|
-
long_description=README,
|
|
56
|
-
license="MIT",
|
|
57
|
-
packages=find_packages(exclude=("tests", "tests.*")),
|
|
58
|
-
package_data={"pelutils": ["py.typed"]},
|
|
59
|
-
author="Asger Laurits Schultz, Søren Winkel Holm",
|
|
60
|
-
author_email="asger.s@protonmail.com, swholm@protonmail.com",
|
|
61
|
-
keywords=["utility", "logger", "parser", "profiling", "plotting"],
|
|
62
|
-
url="https://github.com/peleiden/pelutils",
|
|
63
|
-
download_url="https://pypi.org/project/pelutils/",
|
|
64
|
-
install_requires=requirements,
|
|
65
|
-
extras_require={"docs": requirements_docs, "dev": requirements_dev + requirements_docs},
|
|
66
|
-
entry_points={
|
|
67
|
-
"console_scripts": [
|
|
68
|
-
"linecounter = pelutils._entry_points.linecounter:run",
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
ext_modules=[
|
|
72
|
-
Extension(
|
|
73
|
-
name="_pelutils_c",
|
|
74
|
-
sources=c_files,
|
|
75
|
-
extra_compile_args=["-DMS_WIN64"] if sys.platform == "win32" else [],
|
|
76
|
-
)
|
|
77
|
-
],
|
|
78
|
-
license_files=[os.path.join("pelutils", "_c", "hashmap", "LICENSE")],
|
|
79
|
-
python_requires=">=3.11",
|
|
80
|
-
classifiers=[
|
|
81
|
-
"Intended Audience :: Developers",
|
|
82
|
-
"Operating System :: OS Independent",
|
|
83
|
-
"Programming Language :: Python",
|
|
84
|
-
"Programming Language :: Python :: 3 :: Only",
|
|
85
|
-
"Programming Language :: Python :: Free Threading :: 3 - Stable",
|
|
86
|
-
"Programming Language :: Python :: 3.11",
|
|
87
|
-
"Programming Language :: Python :: 3.12",
|
|
88
|
-
"Programming Language :: Python :: 3.13",
|
|
89
|
-
"Programming Language :: Python :: 3.14",
|
|
90
|
-
"Programming Language :: Python :: 3.15",
|
|
91
|
-
],
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
if __name__ == "__main__":
|
|
95
|
-
setup(**setup_args)
|
|
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
|