arraykit 1.0.9__tar.gz → 1.2.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 (49) hide show
  1. {arraykit-1.0.9 → arraykit-1.2.0}/MANIFEST.in +1 -0
  2. {arraykit-1.0.9/arraykit.egg-info → arraykit-1.2.0}/PKG-INFO +40 -15
  3. {arraykit-1.0.9 → arraykit-1.2.0}/README.rst +16 -0
  4. arraykit-1.2.0/VERSION +2 -0
  5. {arraykit-1.0.9 → arraykit-1.2.0/arraykit.egg-info}/PKG-INFO +40 -15
  6. {arraykit-1.0.9 → arraykit-1.2.0}/arraykit.egg-info/SOURCES.txt +4 -0
  7. arraykit-1.2.0/pyproject.toml +56 -0
  8. arraykit-1.2.0/setup.py +38 -0
  9. {arraykit-1.0.9 → arraykit-1.2.0}/src/__init__.py +4 -1
  10. {arraykit-1.0.9 → arraykit-1.2.0}/src/__init__.pyi +3 -0
  11. {arraykit-1.0.9 → arraykit-1.2.0}/src/_arraykit.c +24 -3
  12. {arraykit-1.0.9 → arraykit-1.2.0}/src/auto_map.c +14 -4
  13. {arraykit-1.0.9 → arraykit-1.2.0}/src/methods.c +133 -0
  14. {arraykit-1.0.9 → arraykit-1.2.0}/src/methods.h +9 -0
  15. {arraykit-1.0.9 → arraykit-1.2.0}/src/utilities.h +63 -9
  16. arraykit-1.2.0/test/test_astype_array.py +123 -0
  17. arraykit-1.2.0/test/test_objectable.py +68 -0
  18. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_pyi.py +2 -0
  19. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_util.py +1 -2
  20. arraykit-1.0.9/setup.py +0 -94
  21. {arraykit-1.0.9 → arraykit-1.2.0}/LICENSE.txt +0 -0
  22. {arraykit-1.0.9 → arraykit-1.2.0}/arraykit.egg-info/dependency_links.txt +0 -0
  23. {arraykit-1.0.9 → arraykit-1.2.0}/arraykit.egg-info/requires.txt +0 -0
  24. {arraykit-1.0.9 → arraykit-1.2.0}/arraykit.egg-info/top_level.txt +0 -0
  25. {arraykit-1.0.9 → arraykit-1.2.0}/setup.cfg +0 -0
  26. {arraykit-1.0.9 → arraykit-1.2.0}/src/array_go.c +0 -0
  27. {arraykit-1.0.9 → arraykit-1.2.0}/src/array_go.h +0 -0
  28. {arraykit-1.0.9 → arraykit-1.2.0}/src/array_to_tuple.c +0 -0
  29. {arraykit-1.0.9 → arraykit-1.2.0}/src/array_to_tuple.h +0 -0
  30. {arraykit-1.0.9 → arraykit-1.2.0}/src/auto_map.h +0 -0
  31. {arraykit-1.0.9 → arraykit-1.2.0}/src/block_index.c +0 -0
  32. {arraykit-1.0.9 → arraykit-1.2.0}/src/block_index.h +0 -0
  33. {arraykit-1.0.9 → arraykit-1.2.0}/src/delimited_to_arrays.c +0 -0
  34. {arraykit-1.0.9 → arraykit-1.2.0}/src/delimited_to_arrays.h +0 -0
  35. {arraykit-1.0.9 → arraykit-1.2.0}/src/py.typed +0 -0
  36. {arraykit-1.0.9 → arraykit-1.2.0}/src/tri_map.c +0 -0
  37. {arraykit-1.0.9 → arraykit-1.2.0}/src/tri_map.h +0 -0
  38. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_array_go.py +0 -0
  39. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_auto_map.py +0 -0
  40. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_auto_map_property.py +0 -0
  41. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_block_index.py +0 -0
  42. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_delimited_to_arrays.py +0 -0
  43. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_delimited_to_arrays_integration.py +0 -0
  44. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_delimited_to_arrays_property.py +0 -0
  45. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_nonzero_1d.py +0 -0
  46. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_nonzero_1d_property.py +0 -0
  47. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_split_after_count.py +0 -0
  48. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_tri_map.py +0 -0
  49. {arraykit-1.0.9 → arraykit-1.2.0}/test/test_type_discovery.py +0 -0
@@ -1,2 +1,3 @@
1
1
  include src/__init__.pyi src/py.typed
2
2
  include src/*.h
3
+ include VERSION
@@ -1,17 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arraykit
3
- Version: 1.0.9
3
+ Version: 1.2.0
4
4
  Summary: Array utilities for StaticFrame
5
- Home-page: https://github.com/static-frame/arraykit
6
5
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
7
- License: MIT
8
- Keywords: numpy array
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/static-frame/arraykit
8
+ Keywords: numpy,array
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Topic :: Software Development
12
12
  Classifier: Programming Language :: C
13
13
  Classifier: Programming Language :: Python :: Implementation :: CPython
14
- Classifier: License :: OSI Approved :: MIT License
15
14
  Classifier: Operating System :: MacOS :: MacOS X
16
15
  Classifier: Operating System :: Microsoft :: Windows
17
16
  Classifier: Operating System :: POSIX
@@ -19,22 +18,32 @@ Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: Free Threading
22
23
  Classifier: Typing :: Typed
23
24
  Requires-Python: >=3.10
24
25
  Description-Content-Type: text/x-rst
25
26
  License-File: LICENSE.txt
26
27
  Requires-Dist: numpy>=1.24.3
27
- Dynamic: author
28
- Dynamic: classifier
29
- Dynamic: description
30
- Dynamic: description-content-type
31
- Dynamic: home-page
32
- Dynamic: keywords
33
- Dynamic: license
34
28
  Dynamic: license-file
35
- Dynamic: requires-dist
36
- Dynamic: requires-python
37
- Dynamic: summary
29
+
30
+
31
+
32
+ .. image:: https://img.shields.io/pypi/pyversions/arraykit.svg
33
+ :target: https://pypi.org/project/arraykit
34
+
35
+ .. image:: https://img.shields.io/pypi/v/arraykit.svg
36
+ :target: https://pypi.org/project/arraykit
37
+
38
+ .. image:: https://img.shields.io/conda/vn/conda-forge/arraykit.svg
39
+ :target: https://anaconda.org/conda-forge/arraykit
40
+
41
+ .. image:: https://img.shields.io/github/actions/workflow/status/static-frame/arraykit/ci.yml?branch=master&label=build&logo=Github
42
+ :target: https://github.com/static-frame/arraykit/actions/workflows/ci.yml
43
+
44
+
45
+ arraykit
46
+ =============
38
47
 
39
48
  The ArrayKit library provides utilities for creating and transforming NumPy arrays, implementing performance-critical StaticFrame operations as Python C extensions.
40
49
 
@@ -55,6 +64,22 @@ ArrayKit requires the following:
55
64
  What is New in ArrayKit
56
65
  -------------------------
57
66
 
67
+ 1.2.0
68
+ ............
69
+
70
+ Now building wheels for Python 3.14 and 3.14t.
71
+
72
+
73
+ 1.1.0
74
+ ............
75
+
76
+ Now building free-threaded compatible wheels for Python 3.13.
77
+
78
+ Added ``is_objectable()`` and ``is_objectable_dt64()``.
79
+
80
+ Added ``astype_array()``.
81
+
82
+
58
83
  1.0.9
59
84
  ............
60
85
 
@@ -35,6 +35,22 @@ ArrayKit requires the following:
35
35
  What is New in ArrayKit
36
36
  -------------------------
37
37
 
38
+ 1.2.0
39
+ ............
40
+
41
+ Now building wheels for Python 3.14 and 3.14t.
42
+
43
+
44
+ 1.1.0
45
+ ............
46
+
47
+ Now building free-threaded compatible wheels for Python 3.13.
48
+
49
+ Added ``is_objectable()`` and ``is_objectable_dt64()``.
50
+
51
+ Added ``astype_array()``.
52
+
53
+
38
54
  1.0.9
39
55
  ............
40
56
 
arraykit-1.2.0/VERSION ADDED
@@ -0,0 +1,2 @@
1
+ 1.2.0
2
+
@@ -1,17 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arraykit
3
- Version: 1.0.9
3
+ Version: 1.2.0
4
4
  Summary: Array utilities for StaticFrame
5
- Home-page: https://github.com/static-frame/arraykit
6
5
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
7
- License: MIT
8
- Keywords: numpy array
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/static-frame/arraykit
8
+ Keywords: numpy,array
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Intended Audience :: Developers
11
11
  Classifier: Topic :: Software Development
12
12
  Classifier: Programming Language :: C
13
13
  Classifier: Programming Language :: Python :: Implementation :: CPython
14
- Classifier: License :: OSI Approved :: MIT License
15
14
  Classifier: Operating System :: MacOS :: MacOS X
16
15
  Classifier: Operating System :: Microsoft :: Windows
17
16
  Classifier: Operating System :: POSIX
@@ -19,22 +18,32 @@ Classifier: Programming Language :: Python :: 3.10
19
18
  Classifier: Programming Language :: Python :: 3.11
20
19
  Classifier: Programming Language :: Python :: 3.12
21
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Programming Language :: Python :: Free Threading
22
23
  Classifier: Typing :: Typed
23
24
  Requires-Python: >=3.10
24
25
  Description-Content-Type: text/x-rst
25
26
  License-File: LICENSE.txt
26
27
  Requires-Dist: numpy>=1.24.3
27
- Dynamic: author
28
- Dynamic: classifier
29
- Dynamic: description
30
- Dynamic: description-content-type
31
- Dynamic: home-page
32
- Dynamic: keywords
33
- Dynamic: license
34
28
  Dynamic: license-file
35
- Dynamic: requires-dist
36
- Dynamic: requires-python
37
- Dynamic: summary
29
+
30
+
31
+
32
+ .. image:: https://img.shields.io/pypi/pyversions/arraykit.svg
33
+ :target: https://pypi.org/project/arraykit
34
+
35
+ .. image:: https://img.shields.io/pypi/v/arraykit.svg
36
+ :target: https://pypi.org/project/arraykit
37
+
38
+ .. image:: https://img.shields.io/conda/vn/conda-forge/arraykit.svg
39
+ :target: https://anaconda.org/conda-forge/arraykit
40
+
41
+ .. image:: https://img.shields.io/github/actions/workflow/status/static-frame/arraykit/ci.yml?branch=master&label=build&logo=Github
42
+ :target: https://github.com/static-frame/arraykit/actions/workflows/ci.yml
43
+
44
+
45
+ arraykit
46
+ =============
38
47
 
39
48
  The ArrayKit library provides utilities for creating and transforming NumPy arrays, implementing performance-critical StaticFrame operations as Python C extensions.
40
49
 
@@ -55,6 +64,22 @@ ArrayKit requires the following:
55
64
  What is New in ArrayKit
56
65
  -------------------------
57
66
 
67
+ 1.2.0
68
+ ............
69
+
70
+ Now building wheels for Python 3.14 and 3.14t.
71
+
72
+
73
+ 1.1.0
74
+ ............
75
+
76
+ Now building free-threaded compatible wheels for Python 3.13.
77
+
78
+ Added ``is_objectable()`` and ``is_objectable_dt64()``.
79
+
80
+ Added ``astype_array()``.
81
+
82
+
58
83
  1.0.9
59
84
  ............
60
85
 
@@ -1,6 +1,8 @@
1
1
  LICENSE.txt
2
2
  MANIFEST.in
3
3
  README.rst
4
+ VERSION
5
+ pyproject.toml
4
6
  setup.py
5
7
  arraykit.egg-info/PKG-INFO
6
8
  arraykit.egg-info/SOURCES.txt
@@ -27,6 +29,7 @@ src/tri_map.c
27
29
  src/tri_map.h
28
30
  src/utilities.h
29
31
  test/test_array_go.py
32
+ test/test_astype_array.py
30
33
  test/test_auto_map.py
31
34
  test/test_auto_map_property.py
32
35
  test/test_block_index.py
@@ -35,6 +38,7 @@ test/test_delimited_to_arrays_integration.py
35
38
  test/test_delimited_to_arrays_property.py
36
39
  test/test_nonzero_1d.py
37
40
  test/test_nonzero_1d_property.py
41
+ test/test_objectable.py
38
42
  test/test_pyi.py
39
43
  test/test_split_after_count.py
40
44
  test/test_tri_map.py
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=64",
4
+ "wheel",
5
+ "numpy>=2.2"
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ name = "arraykit"
11
+ description = "Array utilities for StaticFrame"
12
+ readme = { file = "README.rst", content-type = "text/x-rst" }
13
+ requires-python = ">=3.10"
14
+
15
+ dynamic = ["version"]
16
+
17
+ authors = [
18
+ { name = "Christopher Ariza" },
19
+ { name = "Brandt Bucher" },
20
+ { name = "Charles Burkland" },
21
+ ]
22
+ license = "MIT"
23
+ keywords = ["numpy", "array"]
24
+ dependencies = [
25
+ "numpy>=1.24.3",
26
+ ]
27
+ classifiers = [
28
+ "Development Status :: 5 - Production/Stable",
29
+ "Intended Audience :: Developers",
30
+ "Topic :: Software Development",
31
+ "Programming Language :: C",
32
+ "Programming Language :: Python :: Implementation :: CPython",
33
+ "Operating System :: MacOS :: MacOS X",
34
+ "Operating System :: Microsoft :: Windows",
35
+ "Operating System :: POSIX",
36
+ "Programming Language :: Python :: 3.10",
37
+ "Programming Language :: Python :: 3.11",
38
+ "Programming Language :: Python :: 3.12",
39
+ "Programming Language :: Python :: 3.13",
40
+ "Programming Language :: Python :: 3.14",
41
+ "Programming Language :: Python :: Free Threading",
42
+ "Typing :: Typed",
43
+ ]
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/static-frame/arraykit"
47
+
48
+ [tool.setuptools]
49
+ package-dir = { "arraykit" = "src" }
50
+
51
+ [tool.setuptools.package-data]
52
+ arraykit = ["__init__.pyi", "py.typed"]
53
+
54
+ [tool.setuptools.dynamic]
55
+ version = { file = "VERSION" }
56
+
@@ -0,0 +1,38 @@
1
+ from setuptools import setup, Extension
2
+ from setuptools.command.build_ext import build_ext
3
+ import site, os
4
+ from pathlib import Path
5
+
6
+ AK_VERSION = Path("VERSION").read_text(encoding="utf-8").strip()
7
+
8
+ def get_ext_dir(*components: tp.Iterable[str]) -> tp.Sequence[str]:
9
+ dirs = []
10
+ for sp in site.getsitepackages():
11
+ fp = os.path.join(sp, *components)
12
+ if os.path.exists(fp):
13
+ dirs.append(fp)
14
+ return dirs
15
+
16
+ ext_modules = [
17
+ Extension(
18
+ name="arraykit._arraykit",
19
+ sources=[
20
+ "src/_arraykit.c",
21
+ "src/array_go.c",
22
+ "src/array_to_tuple.c",
23
+ "src/block_index.c",
24
+ "src/delimited_to_arrays.c",
25
+ "src/methods.c",
26
+ "src/tri_map.c",
27
+ "src/auto_map.c",
28
+ ],
29
+
30
+ include_dirs=get_ext_dir('numpy', '_core', 'include') + ['src'],
31
+ library_dirs=get_ext_dir('numpy', '_core', 'lib'),
32
+ define_macros=[("AK_VERSION", AK_VERSION)],
33
+ libraries=["npymath"],
34
+ )
35
+ ]
36
+
37
+ setup(ext_modules=ext_modules)
38
+
@@ -31,7 +31,10 @@ from ._arraykit import slice_to_ascending_slice as slice_to_ascending_slice
31
31
  from ._arraykit import array_to_tuple_array as array_to_tuple_array
32
32
  from ._arraykit import array_to_tuple_iter as array_to_tuple_iter
33
33
  from ._arraykit import nonzero_1d as nonzero_1d
34
-
34
+ from ._arraykit import is_objectable_dt64 as is_objectable_dt64
35
+ from ._arraykit import is_objectable as is_objectable
36
+ from ._arraykit import astype_array as astype_array
35
37
  from ._arraykit import AutoMap as AutoMap
36
38
  from ._arraykit import FrozenAutoMap as FrozenAutoMap
37
39
  from ._arraykit import NonUniqueError as NonUniqueError
40
+
@@ -203,6 +203,9 @@ def get_new_indexers_and_screen(indexers: np.ndarray, positions: np.ndarray) ->
203
203
  def first_true_1d(__array: np.ndarray, *, forward: bool) -> int: ...
204
204
  def first_true_2d(__array: np.ndarray, *, forward: bool, axis: int) -> np.ndarray: ...
205
205
  def nonzero_1d(__array: np.ndarray, /) -> np.ndarray: ...
206
+ def is_objectable_dt64(__array: np.ndarray, /) -> bool: ...
207
+ def is_objectable(__array: np.ndarray, /) -> bool: ...
208
+ def astype_array(__array: np.ndarray, __dtype: np.dtype | None, /) -> np.ndarray: ...
206
209
  def slice_to_ascending_slice(__slice: slice, __size: int) -> slice: ...
207
210
  def array_to_tuple_array(__array: np.ndarray) -> np.ndarray: ...
208
211
  def array_to_tuple_iter(__array: np.ndarray) -> tp.Iterator[tp.Tuple[tp.Any, ...]]: ...
@@ -52,6 +52,9 @@ static PyMethodDef arraykit_methods[] = {
52
52
  NULL},
53
53
  {"count_iteration", count_iteration, METH_O, NULL},
54
54
  {"nonzero_1d", nonzero_1d, METH_O, NULL},
55
+ {"is_objectable_dt64", is_objectable_dt64, METH_O, NULL},
56
+ {"is_objectable", is_objectable, METH_O, NULL},
57
+ {"astype_array", astype_array, METH_VARARGS, NULL},
55
58
  {"isna_element",
56
59
  (PyCFunction)isna_element,
57
60
  METH_VARARGS | METH_KEYWORDS,
@@ -72,7 +75,7 @@ static struct PyModuleDef arraykit_module = {
72
75
  .m_methods = arraykit_methods,
73
76
  };
74
77
 
75
- PyObject *
78
+ PyObject*
76
79
  PyInit__arraykit(void)
77
80
  {
78
81
  import_array();
@@ -95,6 +98,7 @@ PyInit__arraykit(void)
95
98
  return NULL;
96
99
  }
97
100
 
101
+ // store a reference to the deepcopy function
98
102
  PyObject *copy = PyImport_ImportModule("copy");
99
103
  if (copy == NULL) {
100
104
  return NULL;
@@ -105,6 +109,18 @@ PyInit__arraykit(void)
105
109
  return NULL;
106
110
  }
107
111
 
112
+ // store a year dtype object
113
+ PyObject* dt_year_str = PyUnicode_FromString("datetime64[Y]");
114
+ if (!dt_year_str) return NULL;
115
+
116
+ PyArray_Descr* dt_year = NULL;
117
+ if (!PyArray_DescrConverter2(dt_year_str, &dt_year)) {
118
+ Py_DECREF(dt_year_str);
119
+ return NULL;
120
+ }
121
+ Py_DECREF(dt_year_str);
122
+
123
+
108
124
  PyObject *m = PyModule_Create(&arraykit_module);
109
125
  if (!m ||
110
126
  PyModule_AddStringConstant(m, "__version__", Py_STRINGIFY(AK_VERSION)) ||
@@ -128,12 +144,17 @@ PyInit__arraykit(void)
128
144
  PyModule_AddObject(m, "ErrorInitTypeBlocks", ErrorInitTypeBlocks) ||
129
145
  PyModule_AddObject(m, "AutoMap", (PyObject *)&AMType) ||
130
146
  PyModule_AddObject(m, "FrozenAutoMap", (PyObject *)&FAMType) ||
131
- PyModule_AddObject(m, "NonUniqueError", NonUniqueError)
147
+ PyModule_AddObject(m, "NonUniqueError", NonUniqueError) ||
148
+ PyModule_AddObject(m, "dt_year", (PyObject *)dt_year)
132
149
  ){
133
- Py_DECREF(deepcopy);
150
+ Py_XDECREF(deepcopy);
151
+ Py_XDECREF(dt_year);
134
152
  Py_XDECREF(m);
135
153
  return NULL;
136
154
  }
155
+ #ifdef Py_GIL_DISABLED
156
+ PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
157
+ #endif
137
158
  return m;
138
159
  }
139
160
 
@@ -426,8 +426,13 @@ fami_iternext(FAMIObject *self)
426
426
  else {
427
427
  PyObject* t = PyTuple_New(2);
428
428
  if (!t) { return NULL; }
429
+ #if PY_VERSION_HEX >= 0x030D0000 // Python 3.13+
430
+ PyObject* k = PyList_GetItemRef(self->fam->keys, index);
431
+ #else
429
432
  PyObject* k = PyList_GET_ITEM(self->fam->keys, index);
430
- Py_INCREF(k);
433
+ Py_XINCREF(k);
434
+ #endif
435
+ if (!k) { return NULL; }
431
436
  PyTuple_SET_ITEM(t, 0, k);
432
437
  PyTuple_SET_ITEM(t, 1, PyLong_FromSsize_t(index));
433
438
  return t;
@@ -438,8 +443,13 @@ fami_iternext(FAMIObject *self)
438
443
  return PyArray_ToScalar(PyArray_GETPTR1(self->keys_array, index), self->keys_array);
439
444
  }
440
445
  else {
446
+ #if PY_VERSION_HEX >= 0x030D0000 // Python 3.13+
447
+ PyObject* yield = PyList_GetItemRef(self->fam->keys, index);
448
+ #else
441
449
  PyObject* yield = PyList_GET_ITEM(self->fam->keys, index);
442
- Py_INCREF(yield);
450
+ Py_XINCREF(yield);
451
+ #endif
452
+ if (!yield) { return NULL; }
443
453
  return yield;
444
454
  }
445
455
  }
@@ -1302,11 +1312,11 @@ lookup(FAMObject *self, PyObject *key) {
1302
1312
  return self->table[table_pos].keys_pos;
1303
1313
  }
1304
1314
 
1305
- // Insert a key_pos, hash pair into the table. Assumes table already has appropriate size. When inserting a new itme, `hash` is -1, forcing a fresh hash to be computed here. Return 0 on success, -1 on error.
1315
+ // Insert a key_pos, hash pair into the table. Assumes table already has appropriate size. When inserting a new item, `hash` is -1, forcing a fresh hash to be computed here. Return 0 on success, -1 on error.
1306
1316
  static int
1307
1317
  insert_obj(
1308
1318
  FAMObject *self,
1309
- PyObject *key,
1319
+ PyObject *key, // NOTE: a borrowed reference
1310
1320
  Py_ssize_t keys_pos,
1311
1321
  Py_hash_t hash)
1312
1322
  {
@@ -201,6 +201,139 @@ nonzero_1d(PyObject *Py_UNUSED(m), PyObject *a) {
201
201
  return AK_nonzero_1d(array);
202
202
  }
203
203
 
204
+ PyObject*
205
+ is_objectable_dt64(PyObject *m, PyObject *a) {
206
+ AK_CHECK_NUMPY_ARRAY(a);
207
+ PyArrayObject* array = (PyArrayObject*)a;
208
+
209
+ // this returns a new reference
210
+ PyObject* dt_year = PyObject_GetAttrString(m, "dt_year");
211
+ int is_objectable = AK_is_objectable_dt64(array, dt_year);
212
+ Py_DECREF(dt_year);
213
+
214
+ switch (is_objectable) {
215
+ case -1:
216
+ return NULL;
217
+ case 0:
218
+ Py_RETURN_FALSE;
219
+ case 1:
220
+ Py_RETURN_TRUE;
221
+ }
222
+ return NULL;
223
+ }
224
+
225
+
226
+ PyObject*
227
+ is_objectable(PyObject *m, PyObject *a) {
228
+ AK_CHECK_NUMPY_ARRAY(a);
229
+ PyArrayObject* array = (PyArrayObject*)a;
230
+
231
+ char kind = PyArray_DESCR(array)->kind;
232
+ if ((kind == 'M' || kind == 'm')) {
233
+ // this returns a new reference
234
+ PyObject* dt_year = PyObject_GetAttrString(m, "dt_year");
235
+ int is_objectable = AK_is_objectable_dt64(array, dt_year);
236
+ Py_DECREF(dt_year);
237
+
238
+ switch (is_objectable) {
239
+ case -1:
240
+ return NULL;
241
+ case 0:
242
+ Py_RETURN_FALSE;
243
+ case 1:
244
+ Py_RETURN_TRUE;
245
+ }
246
+ }
247
+ Py_RETURN_TRUE;
248
+ }
249
+
250
+ // Convert array to the dtype provided. NOTE: mutable arrays will be returned unless the input array is immutable and no dtype change is needed
251
+ PyObject*
252
+ astype_array(PyObject* m, PyObject* args) {
253
+
254
+ PyObject* a = NULL;
255
+ PyObject* dtype_spec = Py_None;
256
+
257
+ if (!PyArg_ParseTuple(args, "O!|O:astype_array",
258
+ &PyArray_Type, &a,
259
+ &dtype_spec)) {
260
+ return NULL;
261
+ }
262
+ PyArrayObject* array = (PyArrayObject*)a;
263
+
264
+ PyArray_Descr* dtype = NULL;
265
+ if (dtype_spec == Py_None) {
266
+ dtype = PyArray_DescrFromType(NPY_DEFAULT_TYPE);
267
+ } else {
268
+ if (!PyArray_DescrConverter(dtype_spec, &dtype)) {
269
+ return NULL;
270
+ }
271
+ }
272
+
273
+ if (PyArray_EquivTypes(PyArray_DESCR(array), dtype)) {
274
+ Py_DECREF(dtype);
275
+
276
+ if (PyArray_ISWRITEABLE(array)) {
277
+ PyObject* result = PyArray_NewCopy(array, NPY_ANYORDER);
278
+ if (!result) {
279
+ return NULL;
280
+ }
281
+ return result;
282
+ }
283
+ else { // already immutable
284
+ Py_INCREF(a);
285
+ return a;
286
+ }
287
+ }
288
+ // if converting to an object
289
+ if (dtype->type_num == NPY_OBJECT) {
290
+ char kind = PyArray_DESCR(array)->kind;
291
+ if ((kind == 'M' || kind == 'm')) {
292
+ PyObject* dt_year = PyObject_GetAttrString(m, "dt_year");
293
+ int is_objectable = AK_is_objectable_dt64(array, dt_year);
294
+ Py_DECREF(dt_year);
295
+
296
+ if (!is_objectable) {
297
+ PyObject* result = PyArray_NewLikeArray(array, NPY_ANYORDER, dtype, 0);
298
+ if (!result) {
299
+ Py_DECREF(dtype);
300
+ return NULL;
301
+ }
302
+ PyObject** data = (PyObject**)PyArray_DATA((PyArrayObject*)result);
303
+
304
+ PyArrayIterObject* it = (PyArrayIterObject*)PyArray_IterNew(a);
305
+ if (!it) {
306
+ Py_DECREF(result);
307
+ return NULL;
308
+ }
309
+
310
+ npy_intp i = 0;
311
+ while (it->index < it->size) {
312
+ PyObject* item = PyArray_ToScalar(it->dataptr, array);
313
+ if (!item) {
314
+ Py_DECREF(result);
315
+ Py_DECREF(it);
316
+ return NULL;
317
+ }
318
+ data[i++] = item;
319
+ PyArray_ITER_NEXT(it);
320
+ }
321
+ Py_DECREF(it);
322
+ return result;
323
+ }
324
+ }
325
+ }
326
+ // all other cases: do a standard cast conversion
327
+ PyObject* result = PyArray_CastToType(array, dtype, 0);
328
+ if (!result) {
329
+ Py_DECREF(dtype);
330
+ return NULL;
331
+ }
332
+ return result;
333
+ }
334
+
335
+
336
+
204
337
  static char *first_true_1d_kwarg_names[] = {
205
338
  "array",
206
339
  "forward",
@@ -47,6 +47,15 @@ resolve_dtype_iter(PyObject *Py_UNUSED(m), PyObject *arg);
47
47
  PyObject *
48
48
  nonzero_1d(PyObject *Py_UNUSED(m), PyObject *a);
49
49
 
50
+ PyObject *
51
+ is_objectable_dt64(PyObject *m, PyObject *a);
52
+
53
+ PyObject *
54
+ is_objectable(PyObject *m, PyObject *a);
55
+
56
+ PyObject *
57
+ astype_array(PyObject *m, PyObject *args);
58
+
50
59
  PyObject *
51
60
  first_true_1d(PyObject *Py_UNUSED(m), PyObject *args, PyObject *kwargs);
52
61
 
@@ -223,6 +223,69 @@ AK_slice_to_ascending_slice(PyObject* slice, Py_ssize_t size)
223
223
  -step);
224
224
  }
225
225
 
226
+
227
+ static inline NPY_DATETIMEUNIT
228
+ AK_dt_unit_from_array(PyArrayObject* a) {
229
+ // This is based on get_datetime_metadata_from_dtype in the NumPy source, but that function is private. This does not check that the dtype is of the appropriate type.
230
+ PyArray_Descr* dt = PyArray_DESCR(a); // borrowed ref
231
+ PyArray_DatetimeMetaData* dma = &(((PyArray_DatetimeDTypeMetaData *)PyDataType_C_METADATA(dt))->meta);
232
+ return dma->base;
233
+ }
234
+
235
+ // Given a dt64 array, determine if it can be cast to a object without data loss. Returns -1 on error. NOTE: if we use dt_year, must incref first
236
+ static inline int
237
+ AK_is_objectable_dt64(PyArrayObject* a, PyObject* dt_year)
238
+ {
239
+ NPY_DATETIMEUNIT unit = AK_dt_unit_from_array(a);
240
+ switch (unit) {
241
+ case NPY_FR_ERROR:
242
+ case NPY_FR_Y:
243
+ case NPY_FR_M:
244
+ case NPY_FR_W:
245
+ return false;
246
+ case NPY_FR_D:
247
+ case NPY_FR_h:
248
+ case NPY_FR_m:
249
+ case NPY_FR_s:
250
+ case NPY_FR_ms:
251
+ case NPY_FR_us:
252
+ break;
253
+ case NPY_FR_ns:
254
+ case NPY_FR_ps:
255
+ case NPY_FR_fs:
256
+ case NPY_FR_as:
257
+ case NPY_FR_GENERIC:
258
+ return false;
259
+ }
260
+
261
+ Py_INCREF(dt_year);
262
+ PyObject* a_year = PyArray_CastToType(a, (PyArray_Descr*)dt_year, 0);
263
+ if (!a_year) {
264
+ Py_DECREF(dt_year);
265
+ return -1;
266
+ }
267
+
268
+ npy_int64* data = (npy_int64*)PyArray_DATA((PyArrayObject*)a_year);
269
+ npy_intp size = PyArray_SIZE((PyArrayObject*)a_year);
270
+
271
+ for (npy_intp i = 0; i < size; ++i) {
272
+ npy_int64 v = data[i];
273
+ if (v == NPY_DATETIME_NAT) {
274
+ continue;
275
+ }
276
+ // offset: 1-1970, 9999-1970
277
+ if (v < -1969 || v > 8029) {
278
+ Py_DECREF(a_year);
279
+ return 0;
280
+ }
281
+ }
282
+ Py_DECREF(a_year);
283
+ return 1;
284
+ }
285
+
286
+
287
+
288
+
226
289
  // Given a Boolean, contiguous 1D array, return the index positions in an int64 array. Through experimentation it has been verified that doing full-size allocation of memory provides the best performance at all scales. Using NpyIter, or using, bit masks does not improve performance over pointer arithmetic. Prescanning for all empty is very effective. Note that NumPy benefits from first counting the nonzeros, then allocating only enough data for the expexted number of indices.
227
290
  static inline PyObject *
228
291
  AK_nonzero_1d(PyArrayObject* array) {
@@ -319,15 +382,6 @@ AK_nonzero_1d(PyArrayObject* array) {
319
382
  return final;
320
383
  }
321
384
 
322
- static inline NPY_DATETIMEUNIT
323
- AK_dt_unit_from_array(PyArrayObject* a) {
324
- // This is based on get_datetime_metadata_from_dtype in the NumPy source, but that function is private. This does not check that the dtype is of the appropriate type.
325
- PyArray_Descr* dt = PyArray_DESCR(a); // borrowed ref
326
- PyArray_DatetimeMetaData* dma = &(((PyArray_DatetimeDTypeMetaData *)PyDataType_C_METADATA(dt))->meta);
327
- // PyArray_DatetimeMetaData* dma = &(((PyArray_DatetimeDTypeMetaData *)PyArray_DESCR(a)->c_metadata)->meta);
328
- return dma->base;
329
- }
330
-
331
385
  static inline NPY_DATETIMEUNIT
332
386
  AK_dt_unit_from_scalar(PyDatetimeScalarObject* dts) {
333
387
  // Based on convert_pyobject_to_datetime and related usage in datetime.c
@@ -0,0 +1,123 @@
1
+ import unittest
2
+
3
+ import numpy as np
4
+
5
+ from arraykit import astype_array
6
+
7
+ class TestUnit(unittest.TestCase):
8
+
9
+ def test_astype_array_a1(self) -> None:
10
+ a1 = np.array([10, 20, 30], dtype=np.int64)
11
+ a1.flags.writeable = False
12
+
13
+ a2 = astype_array(a1, np.int64)
14
+ self.assertEqual(id(a1), id(a2))
15
+
16
+
17
+ def test_astype_array_a2(self) -> None:
18
+ a1 = np.array([10, 20, 30], dtype=np.int64)
19
+ a1.flags.writeable = False
20
+
21
+ a2 = astype_array(a1, np.float64)
22
+ self.assertNotEqual(id(a1), id(a2))
23
+ self.assertEqual(a2.dtype, np.dtype(np.float64))
24
+
25
+
26
+ def test_astype_array_a3(self) -> None:
27
+ a1 = np.array([False, True, False])
28
+
29
+ a2 = astype_array(a1, np.int8)
30
+ self.assertEqual(a2.dtype, np.dtype(np.int8))
31
+ self.assertTrue(a2.flags.writeable)
32
+
33
+ def test_astype_array_b1(self) -> None:
34
+ a1 = np.array(['2021', '2024'], dtype=np.datetime64)
35
+
36
+ a2 = astype_array(a1, np.object_)
37
+ self.assertEqual(a2.dtype, np.dtype(np.object_))
38
+ self.assertTrue(a2.flags.writeable)
39
+ self.assertEqual(list(a2), [np.datetime64('2021'), np.datetime64('2024')])
40
+
41
+
42
+ def test_astype_array_b2(self) -> None:
43
+ a1 = np.array(['2021', '1642'], dtype=np.datetime64)
44
+
45
+ a2 = astype_array(a1, np.object_)
46
+ self.assertEqual(a2.dtype, np.dtype(np.object_))
47
+ self.assertTrue(a2.flags.writeable)
48
+ self.assertEqual(list(a2), [np.datetime64('2021'), np.datetime64('1642')])
49
+
50
+
51
+ def test_astype_array_b3(self) -> None:
52
+ a1 = np.array(['2021', '2024', '1984', '1642'], dtype=np.datetime64).reshape((2, 2))
53
+
54
+ a2 = astype_array(a1, np.object_)
55
+ self.assertEqual(a2.dtype, np.dtype(np.object_))
56
+ self.assertTrue(a2.flags.writeable)
57
+ self.assertEqual(
58
+ list(list(a) for a in a2),
59
+ [[np.datetime64('2021'), np.datetime64('2024')], [np.datetime64('1984'), np.datetime64('1642')]])
60
+
61
+ def test_astype_array_b4(self) -> None:
62
+ a1 = np.array(['2021', '2024', '1532', '1984', '1642', '899'], dtype=np.datetime64).reshape((2, 3))
63
+
64
+ a2 = astype_array(a1, np.object_)
65
+ self.assertEqual(a2.dtype, np.dtype(np.object_))
66
+ self.assertEqual(a2.shape, (2, 3))
67
+ self.assertTrue(a2.flags.writeable)
68
+ self.assertEqual(
69
+ list(list(a) for a in a2),
70
+ [[np.datetime64('2021'), np.datetime64('2024'), np.datetime64('1532')],
71
+ [np.datetime64('1984'), np.datetime64('1642'), np.datetime64('899')]])
72
+
73
+ def test_astype_array_c(self) -> None:
74
+ with self.assertRaises(TypeError):
75
+ _ = astype_array([3, 4, 5], np.int64)
76
+
77
+
78
+ def test_astype_array_d1(self) -> None:
79
+ a1 = np.array([10, 20, 30], dtype=np.int64)
80
+ a2 = astype_array(a1)
81
+
82
+ self.assertEqual(a2.dtype, np.dtype(np.float64))
83
+ self.assertEqual(a2.shape, (3,))
84
+ self.assertTrue(a2.flags.writeable)
85
+
86
+
87
+ def test_astype_array_d2(self) -> None:
88
+ a1 = np.array([10, 20, 30], dtype=np.int64)
89
+ a2 = astype_array(a1, None)
90
+
91
+ self.assertEqual(a2.dtype, np.dtype(np.float64))
92
+ self.assertEqual(a2.shape, (3,))
93
+ self.assertTrue(a2.flags.writeable)
94
+
95
+
96
+
97
+ def test_astype_array_d3(self) -> None:
98
+ a1 = np.array([10, 20, 30], dtype=np.int64)
99
+ a2 = astype_array(a1, np.int64)
100
+
101
+ self.assertEqual(a2.dtype, np.dtype(np.int64))
102
+ self.assertEqual(a2.shape, (3,))
103
+ self.assertTrue(a2.flags.writeable)
104
+
105
+ self.assertNotEqual(id(a1), id(a2))
106
+
107
+ def test_astype_array_e(self) -> None:
108
+ a1 = np.array(['2021', '2024', '1997', '1984', '2000', '1999'], dtype='datetime64[ns]').reshape((2, 3))
109
+
110
+ a2 = astype_array(a1, np.object_)
111
+ self.assertEqual(a2.dtype, np.dtype(np.object_))
112
+ self.assertEqual(a2.shape, (2, 3))
113
+ self.assertTrue(a2.flags.writeable)
114
+ self.assertEqual(
115
+ list(list(a) for a in a2),
116
+ [[np.datetime64('2021-01-01T00:00:00.000000000'),
117
+ np.datetime64('2024-01-01T00:00:00.000000000'),
118
+ np.datetime64('1997-01-01T00:00:00.000000000')],
119
+ [np.datetime64('1984-01-01T00:00:00.000000000'),
120
+ np.datetime64('2000-01-01T00:00:00.000000000'),
121
+ np.datetime64('1999-01-01T00:00:00.000000000')]]
122
+ )
123
+
@@ -0,0 +1,68 @@
1
+ import unittest
2
+
3
+ import numpy as np
4
+
5
+ from arraykit import is_objectable_dt64
6
+ from arraykit import is_objectable
7
+
8
+ class TestUnit(unittest.TestCase):
9
+
10
+ def test_is_objectable_a1(self) -> None:
11
+ a1 = np.array(['2022-01-04', '1954-04-12'], dtype=np.datetime64)
12
+ self.assertTrue(is_objectable(a1))
13
+
14
+ def test_is_objectable_a2(self) -> None:
15
+ a1 = np.array(['10000-01-04', '1954-04-12'], dtype=np.datetime64)
16
+ self.assertFalse(is_objectable(a1))
17
+
18
+ def test_is_objectable_b(self) -> None:
19
+ a1 = np.array([10, 20])
20
+ self.assertTrue(is_objectable(a1))
21
+
22
+ def test_is_objectable_c(self) -> None:
23
+ a1 = np.array([True, False])
24
+ self.assertTrue(is_objectable(a1))
25
+
26
+ def test_is_objectable_d(self) -> None:
27
+ a1 = np.array(['b', 'ccc'])
28
+ self.assertTrue(is_objectable(a1))
29
+
30
+ def test_is_objectable_e(self) -> None:
31
+ a1 = np.array(['b', None, False], dtype=object)
32
+ self.assertTrue(is_objectable(a1))
33
+
34
+
35
+ #---------------------------------------------------------------------------
36
+
37
+ def test_is_objectable_dt64_a1(self) -> None:
38
+ a1 = np.array(['2022-01-04', '1954-04-12'], dtype=np.datetime64)
39
+ self.assertTrue(is_objectable_dt64(a1))
40
+
41
+ def test_is_objectable_dt64_a2(self) -> None:
42
+ a1 = np.array(['2022-01-04', '', '1954-04-12'], dtype=np.datetime64)
43
+ self.assertTrue(is_objectable_dt64(a1))
44
+
45
+ def test_is_objectable_dt64_a3(self) -> None:
46
+ a1 = np.array(['2022-01-04', '1954-04-12', '', ''], dtype=np.datetime64)
47
+ self.assertTrue(is_objectable_dt64(a1))
48
+
49
+
50
+ def test_is_objectable_dt64_b(self) -> None:
51
+ # years are nevery objectable
52
+ a1 = np.array(['2022', '2023'], dtype=np.datetime64)
53
+ self.assertFalse(is_objectable_dt64(a1))
54
+
55
+
56
+ def test_is_objectable_dt64_c(self) -> None:
57
+ a1 = np.array(['-120-01-01', '2023-04-05'], dtype='datetime64[m]')
58
+ self.assertFalse(is_objectable_dt64(a1))
59
+
60
+ def test_is_objectable_dt64_d(self) -> None:
61
+ a1 = np.array(['2024-01-01', '2023-04-05', '10000-01-01'], dtype='datetime64[s]')
62
+ self.assertFalse(is_objectable_dt64(a1))
63
+
64
+
65
+ def test_is_objectable_dt64_e(self) -> None:
66
+ a1 = np.array(['2024-01-01', '2023-04-05'], dtype='datetime64[ns]')
67
+ self.assertFalse(is_objectable_dt64(a1))
68
+
@@ -14,6 +14,8 @@ class Interface(tp.NamedTuple):
14
14
 
15
15
  @staticmethod
16
16
  def _valid_name(name: str) -> bool:
17
+ if name in ('__annotate__', '__class__', '__annotate_func__'):
18
+ return False
17
19
  if name.startswith('__'):
18
20
  return True
19
21
  if name.startswith('_'):
@@ -17,6 +17,7 @@ from arraykit import mloc
17
17
  from arraykit import immutable_filter
18
18
  from arraykit import array_deepcopy
19
19
  from arraykit import isna_element
20
+ from arraykit import is_objectable_dt64
20
21
  from arraykit import dtype_from_element
21
22
  from arraykit import count_iteration
22
23
  from arraykit import first_true_1d
@@ -953,7 +954,5 @@ class TestUnit(unittest.TestCase):
953
954
  )
954
955
 
955
956
 
956
-
957
-
958
957
  if __name__ == '__main__':
959
958
  unittest.main()
arraykit-1.0.9/setup.py DELETED
@@ -1,94 +0,0 @@
1
- import site
2
- import os
3
- from os import path
4
- import typing as tp
5
- from setuptools import Extension # type: ignore
6
- from setuptools import setup
7
-
8
- AK_VERSION = '1.0.9'
9
-
10
- ROOT_DIR_FP = path.abspath(path.dirname(__file__))
11
-
12
- def get_long_description() -> str:
13
- with open(path.join(ROOT_DIR_FP, 'README.rst'), encoding='utf-8') as f:
14
- msg = []
15
- collect = False
16
- start = -1
17
- for i, line in enumerate(f):
18
- if line.startswith('arraykit'):
19
- start = i + 2 # skip this line and the next
20
- if i == start:
21
- collect = True
22
- if collect:
23
- msg.append(line)
24
- return ''.join(msg).strip()
25
-
26
-
27
- # NOTE: we do this to avoid importing numpy: https://stackoverflow.com/questions/54117786/add-numpy-get-include-argument-to-setuptools-without-preinstalled-numpy
28
- # we used to import the following to get directories:
29
- # from numpy.distutils.misc_util import get_info
30
- # import numpy as np # type: ignore
31
- # get_info('npymath')['library_dirs']
32
- # get_info('npymath')['libraries']
33
-
34
- def get_ext_dir(*components: tp.Iterable[str]) -> tp.Sequence[str]:
35
- dirs = []
36
- for sp in site.getsitepackages():
37
- fp = os.path.join(sp, *components)
38
- if os.path.exists(fp):
39
- dirs.append(fp)
40
- return dirs
41
-
42
- ak_extension = Extension(
43
- name='arraykit._arraykit', # build into module
44
- sources=[
45
- 'src/_arraykit.c',
46
- 'src/array_go.c',
47
- 'src/array_to_tuple.c',
48
- 'src/block_index.c',
49
- 'src/delimited_to_arrays.c',
50
- 'src/methods.c',
51
- 'src/tri_map.c',
52
- 'src/auto_map.c',
53
- ],
54
- include_dirs=get_ext_dir('numpy', '_core', 'include') + ['src'],
55
- library_dirs=get_ext_dir('numpy', '_core', 'lib'),
56
- define_macros=[("AK_VERSION", AK_VERSION)],
57
- libraries=['npymath'], # not including mlib at this time
58
- )
59
-
60
- setup(
61
- name='arraykit',
62
- version=AK_VERSION,
63
- description='Array utilities for StaticFrame',
64
- long_description=get_long_description(),
65
- long_description_content_type='text/x-rst', # use text/x-rst
66
- python_requires='>=3.10',
67
- install_requires=['numpy>=1.24.3'],
68
- url='https://github.com/static-frame/arraykit',
69
- author='Christopher Ariza, Brandt Bucher, Charles Burkland',
70
- license='MIT',
71
- # See https://pypi.python.org/pypi?%3Aaction=list_classifiers
72
- classifiers=[
73
- 'Development Status :: 5 - Production/Stable',
74
- 'Intended Audience :: Developers',
75
- 'Topic :: Software Development',
76
- "Programming Language :: C",
77
- "Programming Language :: Python :: Implementation :: CPython",
78
- 'License :: OSI Approved :: MIT License',
79
- 'Operating System :: MacOS :: MacOS X',
80
- 'Operating System :: Microsoft :: Windows',
81
- 'Operating System :: POSIX',
82
- 'Programming Language :: Python :: 3.10',
83
- 'Programming Language :: Python :: 3.11',
84
- 'Programming Language :: Python :: 3.12',
85
- 'Programming Language :: Python :: 3.13',
86
- 'Typing :: Typed',
87
- ],
88
- keywords='numpy array',
89
- packages=['arraykit'],
90
- package_dir={'arraykit': 'src'},
91
- package_data={'arraykit': ['__init__.pyi', 'py.typed']},
92
- include_package_data=True,
93
- ext_modules=[ak_extension],
94
- )
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