pelutils 3.8.4__tar.gz → 3.9.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.
Files changed (50) hide show
  1. {pelutils-3.8.4 → pelutils-3.9.0}/PKG-INFO +2 -2
  2. {pelutils-3.8.4 → pelutils-3.9.0}/README.md +1 -1
  3. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/__init__.py +1 -1
  4. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/__version__.py +1 -1
  5. pelutils-3.9.0/pelutils/_utils.py +46 -0
  6. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/datastorage2/_pretty_json.py +11 -0
  7. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/ds/__init__.py +21 -9
  8. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/types.py +5 -0
  9. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils.egg-info/PKG-INFO +2 -2
  10. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils.egg-info/SOURCES.txt +3 -1
  11. pelutils-3.9.0/tests/test_types.py +37 -0
  12. pelutils-3.9.0/tests/test_utils.py +13 -0
  13. pelutils-3.8.4/tests/test_types.py +0 -22
  14. {pelutils-3.8.4 → pelutils-3.9.0}/MANIFEST.in +0 -0
  15. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_c/__init__.py +0 -0
  16. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_c/ds.c +0 -0
  17. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_c/hashmap.c/LICENSE +0 -0
  18. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_c/hashmap.c/hashmap.c +0 -0
  19. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_c/hashmap.c/hashmap.h +0 -0
  20. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_entry_points/__init__.py +0 -0
  21. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/_entry_points/linecounter.py +0 -0
  22. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/datastorage.py +0 -0
  23. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/datastorage2/__init__.py +0 -0
  24. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/ds/distributions.py +0 -0
  25. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/ds/plots.py +0 -0
  26. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/ds/stats.py +0 -0
  27. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/format.py +0 -0
  28. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/jsonl.py +0 -0
  29. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/logging/__init__.py +0 -0
  30. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/logging/_rotate.py +0 -0
  31. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/logging/_utils.py +0 -0
  32. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/parser.py +0 -0
  33. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/pretty_json.py +0 -0
  34. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/py.typed +0 -0
  35. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/tests.py +0 -0
  36. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils/ticktock.py +0 -0
  37. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils.egg-info/dependency_links.txt +0 -0
  38. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils.egg-info/entry_points.txt +0 -0
  39. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils.egg-info/requires.txt +0 -0
  40. {pelutils-3.8.4 → pelutils-3.9.0}/pelutils.egg-info/top_level.txt +0 -0
  41. {pelutils-3.8.4 → pelutils-3.9.0}/setup.cfg +0 -0
  42. {pelutils-3.8.4 → pelutils-3.9.0}/setup.py +0 -0
  43. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_datastorage.py +0 -0
  44. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_format.py +0 -0
  45. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_init.py +0 -0
  46. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_jsonl.py +0 -0
  47. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_parser.py +0 -0
  48. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_pretty_json.py +0 -0
  49. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_tests.py +0 -0
  50. {pelutils-3.8.4 → pelutils-3.9.0}/tests/test_ticktock.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pelutils
3
- Version: 3.8.4
3
+ Version: 3.9.0
4
4
  Summary: The Swiss army knife of Python projects.
5
5
  Home-page: https://github.com/peleiden/pelutils
6
6
  Download-URL: https://pypi.org/project/pelutils/
@@ -275,7 +275,7 @@ log2.configure("path/to/save/log2.log")
275
275
 
276
276
  A few different numpy types are defined for the convenience of not having to remember what data types your arrays are - and also to satisfy your nasty type checkers.
277
277
  ```py
278
- from pelutils.types import AnyArray, FloatArray, IntArray
278
+ from pelutils.types import AnyArray, BoolArray, BytesArray, ComplexArray, FloatArray, IntArray, ObjectArray, StringArray
279
279
 
280
280
 
281
281
  def function_which_takes_np_types(
@@ -250,7 +250,7 @@ log2.configure("path/to/save/log2.log")
250
250
 
251
251
  A few different numpy types are defined for the convenience of not having to remember what data types your arrays are - and also to satisfy your nasty type checkers.
252
252
  ```py
253
- from pelutils.types import AnyArray, FloatArray, IntArray
253
+ from pelutils.types import AnyArray, BoolArray, BytesArray, ComplexArray, FloatArray, IntArray, ObjectArray, StringArray
254
254
 
255
255
 
256
256
  def function_which_takes_np_types(
@@ -24,7 +24,6 @@ import numpy as np
24
24
  import psutil
25
25
 
26
26
  try:
27
- # torch is only to be installed if pelutils[ds] has been installed
28
27
  import torch
29
28
 
30
29
  _has_torch = True
@@ -315,6 +314,7 @@ from .ticktock import * # noqa: F403
315
314
  TT: TickTock # noqa: F405
316
315
  from .datastorage import * # noqa: F403
317
316
  from .datastorage2 import * # noqa: F403
317
+ from .ds import unique
318
318
  from .format import * # noqa: F403
319
319
  from .jsonl import * # noqa: F403
320
320
  from .pretty_json import * # noqa: F403
@@ -1,2 +1,2 @@
1
1
  # Do not put anything else in this file
2
- __version__ = "3.8.4"
2
+ __version__ = "3.9.0"
@@ -0,0 +1,46 @@
1
+ import sys
2
+ from typing import Any
3
+
4
+
5
+ def isinstance_by_name(obj: Any, module: str, qualname: str) -> bool: # pyright: ignore[reportExplicitAny]
6
+ """Check whether ``obj`` is an instance of a class without importing its module.
7
+
8
+ This is useful for libraries with optional dependencies: if the dependency
9
+ has not been imported anywhere in the current process, ``obj`` cannot
10
+ possibly be an instance of one of its classes, so we can skip the check
11
+ without forcing an import.
12
+
13
+ Parameters
14
+ ----------
15
+ obj
16
+ The object whose type is being checked.
17
+ module
18
+ The fully qualified name of the module that defines the target class
19
+ (e.g. ``"pandas"`` or ``"numpy"``).
20
+ qualname
21
+ The attribute name of the class within ``module`` (e.g. ``"Series"``
22
+ or ``"ndarray"``). Dotted paths are not supported.
23
+
24
+ Returns
25
+ -------
26
+ bool
27
+ ``True`` if ``module`` is already loaded in :data:`sys.modules` and
28
+ ``obj`` is an instance of ``module.qualname``; ``False`` otherwise
29
+ (including when the module is not loaded or does not expose the
30
+ named attribute).
31
+
32
+ Examples
33
+ --------
34
+ >>> import pandas as pd
35
+ >>> _isinstance_by_name(pd.Series([1, 2, 3]), "pandas", "Series")
36
+ True
37
+ >>> _isinstance_by_name([1, 2, 3], "pandas", "Series")
38
+ False
39
+ """
40
+ mod = sys.modules.get(module)
41
+ if mod is None:
42
+ return False
43
+ cls = getattr(mod, qualname, None)
44
+ if not isinstance(cls, type):
45
+ return False
46
+ return isinstance(obj, cls)
@@ -9,6 +9,13 @@ import json
9
9
  import pickle
10
10
  from typing import Any
11
11
 
12
+ try:
13
+ import torch
14
+
15
+ _has_torch = True
16
+ except ModuleNotFoundError:
17
+ _has_torch = False
18
+
12
19
  # Sentinel prefix so consumers can detect pickled blobs.
13
20
  _PICKLE_PREFIX = "__pickled_b64__"
14
21
 
@@ -45,6 +52,10 @@ def _get_qualified_type_name(obj: object) -> str:
45
52
 
46
53
  def _pickle_encode(value: object) -> str:
47
54
  """Pickle *value*, base64-encode the bytes, and return a prefixed string."""
55
+ if _has_torch and isinstance(value, torch.Tensor): # pyright: ignore[reportPossiblyUnboundVariable]
56
+ # If a view of torch Tensor is given, the whole tensor is pickled, not just the view
57
+ # Therefore, it is cloned first to ensure that only the view itself is pickled
58
+ value = value.clone()
48
59
  raw = pickle.dumps(value, protocol=pickle.HIGHEST_PROTOCOL)
49
60
  encoded = base64.b64encode(raw).decode("ascii")
50
61
  return f"{_PICKLE_PREFIX}:{_get_qualified_type_name(value)}:{encoded}"
@@ -5,7 +5,8 @@ from typing import TypeVar, Union, cast
5
5
 
6
6
  import numpy as np
7
7
 
8
- from pelutils.types import AnyArray, FloatArray, IntArray
8
+ from pelutils._utils import isinstance_by_name
9
+ from pelutils.types import AnyArray, BoolArray, BytesArray, FloatArray, IntArray, StringArray
9
10
 
10
11
  try:
11
12
  import torch
@@ -18,27 +19,38 @@ import _pelutils_c as _c
18
19
 
19
20
  import pelutils._c as _c_utils
20
21
 
21
- ArrayOrTensor = TypeVar("ArrayOrTensor", bound=Union[AnyArray, torch.Tensor])
22
+ ArrayOrTensor = Union["BoolArray | BytesArray | FloatArray | IntArray | StringArray | torch.Tensor"]
22
23
 
23
24
 
24
- def unique(
25
- array: ArrayOrTensor,
25
+ def unique( # noqa: PLR0912
26
+ array: ...,
26
27
  *,
27
28
  return_index: bool = False,
28
29
  return_inverse: bool = False,
29
30
  return_counts: bool = False,
30
31
  axis: int = 0,
31
32
  ) -> ArrayOrTensor | tuple[ArrayOrTensor, ...]:
32
- """Similar to np.unique, but in linear time and returns unsorted."""
33
- if not array.size:
34
- raise ValueError("Array must be non-empty")
33
+ """Return unique elements in a given numpy array (or torch tensor or pandas series).
35
34
 
35
+ This function works very similar to np.unique, but it runs in linear time, making it significantly faster
36
+ for large arrays. The returned unique elements are unsorted, however.
37
+ """
36
38
  is_tensor = False
37
39
  if _has_torch and isinstance(array, torch.Tensor): # pyright: ignore[reportPossiblyUnboundVariable]
38
40
  is_tensor = True
39
41
  np_array = array.numpy()
40
- else:
42
+ elif isinstance_by_name(array, "pandas", "Series"):
43
+ np_array = array.values
44
+ elif isinstance(array, np.ndarray):
45
+ if np.issubdtype(array.dtype, np.object_):
46
+ raise TypeError(f"Unsupported array dtype {array.dtype}")
41
47
  np_array = array
48
+ else:
49
+ raise TypeError(f"Unsupported array type {type(array)}, must be numpy array, torch tensor, or pandas dataframe.")
50
+
51
+ if np_array.ndim == 0:
52
+ raise ValueError("unique does not work for shape-less arrays")
53
+
42
54
  np_array = cast(AnyArray, np_array)
43
55
  del array # Prevent reuse - underlying tensor already referenced by np_array, which should be used from here
44
56
 
@@ -82,7 +94,7 @@ def unique(
82
94
  if _has_torch and is_tensor:
83
95
  ret = [torch.from_numpy(x) for x in ret] # pyright: ignore[reportPossiblyUnboundVariable]
84
96
 
85
- return tuple(ret) if len(ret) > 1 else ret[0] # pyright: ignore[reportReturnType]
97
+ return tuple(ret) if len(ret) > 1 else ret[0]
86
98
 
87
99
 
88
100
  def tensor_bytes(x: AnyArray | torch.Tensor) -> int:
@@ -5,5 +5,10 @@ import numpy.typing as npt
5
5
 
6
6
  # Common numpy types
7
7
  AnyArray = npt.NDArray[Any] # pyright: ignore[reportExplicitAny]
8
+ BoolArray = npt.NDArray[np.bool_]
9
+ BytesArray = npt.NDArray[np.bytes_]
10
+ ComplexArray = npt.NDArray[np.complexfloating[Any]] # pyright: ignore[reportExplicitAny, reportInvalidTypeArguments]
8
11
  FloatArray = npt.NDArray[np.floating[Any]] # pyright: ignore[reportExplicitAny]
9
12
  IntArray = npt.NDArray[np.integer[Any]] # pyright: ignore[reportExplicitAny]
13
+ ObjectArray = npt.NDArray[np.object_]
14
+ StringArray = npt.NDArray[np.str_]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pelutils
3
- Version: 3.8.4
3
+ Version: 3.9.0
4
4
  Summary: The Swiss army knife of Python projects.
5
5
  Home-page: https://github.com/peleiden/pelutils
6
6
  Download-URL: https://pypi.org/project/pelutils/
@@ -275,7 +275,7 @@ log2.configure("path/to/save/log2.log")
275
275
 
276
276
  A few different numpy types are defined for the convenience of not having to remember what data types your arrays are - and also to satisfy your nasty type checkers.
277
277
  ```py
278
- from pelutils.types import AnyArray, FloatArray, IntArray
278
+ from pelutils.types import AnyArray, BoolArray, BytesArray, ComplexArray, FloatArray, IntArray, ObjectArray, StringArray
279
279
 
280
280
 
281
281
  def function_which_takes_np_types(
@@ -3,6 +3,7 @@ README.md
3
3
  setup.py
4
4
  pelutils/__init__.py
5
5
  pelutils/__version__.py
6
+ pelutils/_utils.py
6
7
  pelutils/datastorage.py
7
8
  pelutils/format.py
8
9
  pelutils/jsonl.py
@@ -42,4 +43,5 @@ tests/test_parser.py
42
43
  tests/test_pretty_json.py
43
44
  tests/test_tests.py
44
45
  tests/test_ticktock.py
45
- tests/test_types.py
46
+ tests/test_types.py
47
+ tests/test_utils.py
@@ -0,0 +1,37 @@
1
+ import numpy as np
2
+
3
+ from pelutils import AnyArray, BoolArray, ComplexArray, FloatArray, IntArray, StringArray
4
+ from pelutils.types import BytesArray, ObjectArray
5
+
6
+
7
+ def function_which_takes_np_types( # noqa: PLR0913
8
+ any_array: AnyArray,
9
+ bool_array: BoolArray,
10
+ bytes_array: BytesArray,
11
+ complex_array: ComplexArray,
12
+ float_array: FloatArray,
13
+ int_array: IntArray,
14
+ object_array: ObjectArray,
15
+ string_array: StringArray,
16
+ ) -> ComplexArray:
17
+ assert np.issubdtype(object_array.dtype, np.object_)
18
+ assert not np.issubdtype(bytes_array.dtype, np.object_)
19
+ assert not np.issubdtype(string_array.dtype, np.object_)
20
+ assert not np.issubdtype(float_array.dtype, np.object_)
21
+ return any_array + bool_array + complex_array + float_array + int_array + len(bytes_array) + len(string_array)
22
+
23
+
24
+ def test_types():
25
+ """There isn't much to test.
26
+
27
+ Just make sure that using the types doesn't break anything silly like function declarations.
28
+ """
29
+ any_array = np.arange(5, dtype=np.float16)
30
+ bool_array = np.arange(5).astype(bool)
31
+ bytes_array = np.array([x.to_bytes(x, "little") for x in list(range(5))])
32
+ complex_array = 1j * np.arange(5)
33
+ float_array = np.arange(5, dtype=np.float32)
34
+ int_array = np.arange(5)
35
+ string_array = np.array([str(x) for x in list(range(5))])
36
+ object_array = np.array([str, "", 1, 2, object, object()])
37
+ function_which_takes_np_types(any_array, bool_array, bytes_array, complex_array, float_array, int_array, object_array, string_array)
@@ -0,0 +1,13 @@
1
+ import pandas as pd
2
+ from numpy import array, ndarray
3
+
4
+ from pelutils._utils import isinstance_by_name
5
+
6
+
7
+ def test_isinstance_by_name():
8
+ assert not isinstance_by_name(array([1, 2, 3]), "np", "ndarray")
9
+ assert isinstance_by_name(array([1, 2, 3]), "numpy", "ndarray")
10
+ assert isinstance_by_name(pd.DataFrame({"a": [1]}), "pandas", "DataFrame")
11
+ assert isinstance_by_name(pd.Series([1]), "pandas", "Series")
12
+ assert not isinstance_by_name(pd.Series([1]), "pd", "Series")
13
+ assert not isinstance_by_name(ndarray, "np", "ndarray")
@@ -1,22 +0,0 @@
1
- import numpy as np
2
-
3
- from pelutils.types import AnyArray, FloatArray, IntArray
4
-
5
-
6
- def function_which_takes_np_types(
7
- any_array: AnyArray,
8
- float_array: FloatArray,
9
- int_array: IntArray,
10
- ) -> FloatArray:
11
- return any_array + float_array + int_array
12
-
13
-
14
- def test_types():
15
- """There isn't much to test.
16
-
17
- Just make sure that using the types doesn't break anything silly like function declarations.
18
- """
19
- any_array = np.arange(5, dtype=np.float16)
20
- float_array = np.arange(5, dtype=np.float32)
21
- int_array = np.arange(5)
22
- function_which_takes_np_types(any_array, float_array, int_array)
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