icu4py 0.2.0__cp312-cp312-musllinux_1_2_aarch64.whl
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.
- icu4py/__init__.py +22 -0
- icu4py/_version.cpp +57 -0
- icu4py/_version.cpython-312-aarch64-linux-musl.so +0 -0
- icu4py/_version.pyi +4 -0
- icu4py/locale.cpp +267 -0
- icu4py/locale.cpython-312-aarch64-linux-musl.so +0 -0
- icu4py/locale.pyi +26 -0
- icu4py/messageformat.cpp +522 -0
- icu4py/messageformat.cpython-312-aarch64-linux-musl.so +0 -0
- icu4py/messageformat.pyi +13 -0
- icu4py/py.typed +0 -0
- icu4py-0.2.0.dist-info/METADATA +60 -0
- icu4py-0.2.0.dist-info/RECORD +22 -0
- icu4py-0.2.0.dist-info/WHEEL +5 -0
- icu4py-0.2.0.dist-info/licenses/LICENSE +21 -0
- icu4py-0.2.0.dist-info/sboms/auditwheel.cdx.json +1 -0
- icu4py-0.2.0.dist-info/top_level.txt +1 -0
- icu4py.libs/libgcc_s-2d945d6c.so.1 +0 -0
- icu4py.libs/libicudata-3b40cc52.so.76.1 +0 -0
- icu4py.libs/libicui18n-3545b8ef.so.76.1 +0 -0
- icu4py.libs/libicuuc-130fd364.so.76.1 +0 -0
- icu4py.libs/libstdc++-85f2cd6d.so.6.0.33 +0 -0
icu4py/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from icu4py._version import icu_version, icu_version_info
|
|
7
|
+
else:
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
def __getattr__(name: str) -> Any:
|
|
11
|
+
if name == "icu_version":
|
|
12
|
+
from icu4py._version import icu_version
|
|
13
|
+
|
|
14
|
+
return icu_version
|
|
15
|
+
elif name == "icu_version_info":
|
|
16
|
+
from icu4py._version import icu_version_info
|
|
17
|
+
|
|
18
|
+
return icu_version_info
|
|
19
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ["icu_version", "icu_version_info"]
|
icu4py/_version.cpp
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#define PY_SSIZE_T_CLEAN
|
|
2
|
+
#include <Python.h>
|
|
3
|
+
#include <unicode/uversion.h>
|
|
4
|
+
|
|
5
|
+
namespace {
|
|
6
|
+
|
|
7
|
+
PyMethodDef version_module_methods[] = {
|
|
8
|
+
{nullptr, nullptr, 0, nullptr}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
int icu4py_version_exec(PyObject* m) {
|
|
12
|
+
UVersionInfo versionArray;
|
|
13
|
+
u_getVersion(versionArray);
|
|
14
|
+
|
|
15
|
+
PyObject* version_tuple = Py_BuildValue("(iiii)",
|
|
16
|
+
versionArray[0],
|
|
17
|
+
versionArray[1],
|
|
18
|
+
versionArray[2],
|
|
19
|
+
versionArray[3]
|
|
20
|
+
);
|
|
21
|
+
if (PyModule_AddObject(m, "icu_version_info", version_tuple) < 0) {
|
|
22
|
+
Py_DECREF(version_tuple);
|
|
23
|
+
return -1;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (PyModule_AddStringConstant(m, "icu_version", U_ICU_VERSION) < 0) {
|
|
27
|
+
return -1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
PyModuleDef_Slot version_slots[] = {
|
|
34
|
+
{Py_mod_exec, reinterpret_cast<void*>(icu4py_version_exec)},
|
|
35
|
+
#ifdef Py_GIL_DISABLED
|
|
36
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
37
|
+
#endif
|
|
38
|
+
{0, nullptr}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
static PyModuleDef versionmodule = {
|
|
42
|
+
PyModuleDef_HEAD_INIT,
|
|
43
|
+
"icu4py._version",
|
|
44
|
+
"ICU version information",
|
|
45
|
+
0,
|
|
46
|
+
version_module_methods,
|
|
47
|
+
version_slots,
|
|
48
|
+
nullptr,
|
|
49
|
+
nullptr,
|
|
50
|
+
nullptr,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
} // anonymous namespace
|
|
54
|
+
|
|
55
|
+
PyMODINIT_FUNC PyInit__version() {
|
|
56
|
+
return PyModuleDef_Init(&versionmodule);
|
|
57
|
+
}
|
|
Binary file
|
icu4py/_version.pyi
ADDED
icu4py/locale.cpp
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#define PY_SSIZE_T_CLEAN
|
|
2
|
+
#include <Python.h>
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <unicode/locid.h>
|
|
5
|
+
#include <unicode/strenum.h>
|
|
6
|
+
#include "locale_types.h"
|
|
7
|
+
|
|
8
|
+
namespace {
|
|
9
|
+
|
|
10
|
+
using icu::Locale;
|
|
11
|
+
using icu4py::LocaleObject;
|
|
12
|
+
|
|
13
|
+
void Locale_dealloc(LocaleObject* self) {
|
|
14
|
+
delete self->locale;
|
|
15
|
+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
PyObject* Locale_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
|
|
19
|
+
auto* self = reinterpret_cast<LocaleObject*>(type->tp_alloc(type, 0));
|
|
20
|
+
if (self != nullptr) {
|
|
21
|
+
self->locale = nullptr;
|
|
22
|
+
}
|
|
23
|
+
return reinterpret_cast<PyObject*>(self);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
int Locale_init(LocaleObject* self, PyObject* args, PyObject* kwds) {
|
|
27
|
+
const char* language = nullptr;
|
|
28
|
+
const char* country = nullptr;
|
|
29
|
+
const char* variant = nullptr;
|
|
30
|
+
PyObject* extensions = nullptr;
|
|
31
|
+
|
|
32
|
+
static const char* kwlist[] = {"language", "country", "variant", "extensions", nullptr};
|
|
33
|
+
|
|
34
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|zzO",
|
|
35
|
+
const_cast<char**>(kwlist),
|
|
36
|
+
&language, &country, &variant, &extensions)) {
|
|
37
|
+
return -1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const char* keywords_and_values = nullptr;
|
|
41
|
+
std::string keywords_str;
|
|
42
|
+
|
|
43
|
+
if (extensions != nullptr && extensions != Py_None) {
|
|
44
|
+
if (!PyDict_Check(extensions)) {
|
|
45
|
+
PyErr_SetString(PyExc_TypeError, "extensions must be a dict or None");
|
|
46
|
+
return -1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Py_ssize_t pos = 0;
|
|
50
|
+
PyObject* key;
|
|
51
|
+
PyObject* value;
|
|
52
|
+
bool first = true;
|
|
53
|
+
bool err = false;
|
|
54
|
+
|
|
55
|
+
#ifdef Py_GIL_DISABLED
|
|
56
|
+
Py_BEGIN_CRITICAL_SECTION(extensions);
|
|
57
|
+
#endif
|
|
58
|
+
|
|
59
|
+
while (PyDict_Next(extensions, &pos, &key, &value)) {
|
|
60
|
+
if (!PyUnicode_Check(key)) {
|
|
61
|
+
PyErr_SetString(PyExc_TypeError, "extension keys must be strings");
|
|
62
|
+
err = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
if (!PyUnicode_Check(value)) {
|
|
66
|
+
PyErr_SetString(PyExc_TypeError, "extension values must be strings");
|
|
67
|
+
err = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const char* key_str = PyUnicode_AsUTF8(key);
|
|
72
|
+
if (key_str == nullptr) {
|
|
73
|
+
err = true;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
const char* value_str = PyUnicode_AsUTF8(value);
|
|
77
|
+
if (value_str == nullptr) {
|
|
78
|
+
err = true;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!first) {
|
|
83
|
+
keywords_str += ";";
|
|
84
|
+
}
|
|
85
|
+
keywords_str += key_str;
|
|
86
|
+
keywords_str += "=";
|
|
87
|
+
keywords_str += value_str;
|
|
88
|
+
first = false;
|
|
89
|
+
}
|
|
90
|
+
#ifdef Py_GIL_DISABLED
|
|
91
|
+
Py_END_CRITICAL_SECTION();
|
|
92
|
+
#endif
|
|
93
|
+
if (err) {
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!keywords_str.empty()) {
|
|
98
|
+
keywords_and_values = keywords_str.c_str();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
self->locale = new Locale(language, country, variant, keywords_and_values);
|
|
103
|
+
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
PyObject* Locale_get_bogus(LocaleObject* self, void* closure) {
|
|
108
|
+
if (self->locale->isBogus()) {
|
|
109
|
+
Py_RETURN_TRUE;
|
|
110
|
+
}
|
|
111
|
+
Py_RETURN_FALSE;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
PyObject* Locale_get_language(LocaleObject* self, void* closure) {
|
|
115
|
+
const char* language = self->locale->getLanguage();
|
|
116
|
+
return PyUnicode_FromString(language);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
PyObject* Locale_get_country(LocaleObject* self, void* closure) {
|
|
120
|
+
const char* country = self->locale->getCountry();
|
|
121
|
+
return PyUnicode_FromString(country);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
PyObject* Locale_get_variant(LocaleObject* self, void* closure) {
|
|
125
|
+
const char* variant = self->locale->getVariant();
|
|
126
|
+
return PyUnicode_FromString(variant);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
PyObject* Locale_get_extensions(LocaleObject* self, void* closure) {
|
|
130
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
131
|
+
icu::StringEnumeration* keywords = self->locale->createKeywords(status);
|
|
132
|
+
|
|
133
|
+
if (U_FAILURE(status) || keywords == nullptr) {
|
|
134
|
+
return PyDict_New();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
PyObject* dict = PyDict_New();
|
|
138
|
+
if (dict == nullptr) {
|
|
139
|
+
delete keywords;
|
|
140
|
+
return nullptr;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const char* keyword;
|
|
144
|
+
while ((keyword = keywords->next(nullptr, status)) != nullptr) {
|
|
145
|
+
if (U_FAILURE(status)) {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
std::string value_str;
|
|
150
|
+
const int32_t capacity = 256;
|
|
151
|
+
char buffer[capacity];
|
|
152
|
+
|
|
153
|
+
int32_t length = self->locale->getKeywordValue(keyword, buffer, capacity, status);
|
|
154
|
+
if (U_FAILURE(status)) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
value_str.assign(buffer, length);
|
|
159
|
+
|
|
160
|
+
PyObject* key_obj = PyUnicode_FromString(keyword);
|
|
161
|
+
PyObject* value_obj = PyUnicode_FromString(value_str.c_str());
|
|
162
|
+
|
|
163
|
+
if (key_obj == nullptr || value_obj == nullptr) {
|
|
164
|
+
Py_XDECREF(key_obj);
|
|
165
|
+
Py_XDECREF(value_obj);
|
|
166
|
+
Py_DECREF(dict);
|
|
167
|
+
delete keywords;
|
|
168
|
+
return nullptr;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (PyDict_SetItem(dict, key_obj, value_obj) < 0) {
|
|
172
|
+
Py_DECREF(key_obj);
|
|
173
|
+
Py_DECREF(value_obj);
|
|
174
|
+
Py_DECREF(dict);
|
|
175
|
+
delete keywords;
|
|
176
|
+
return nullptr;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Py_DECREF(key_obj);
|
|
180
|
+
Py_DECREF(value_obj);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
delete keywords;
|
|
184
|
+
|
|
185
|
+
if (U_FAILURE(status)) {
|
|
186
|
+
Py_DECREF(dict);
|
|
187
|
+
PyErr_SetString(PyExc_RuntimeError, "Failed to retrieve keywords");
|
|
188
|
+
return nullptr;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return dict;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
PyGetSetDef Locale_getsetters[] = {
|
|
195
|
+
{const_cast<char*>("bogus"), reinterpret_cast<getter>(Locale_get_bogus), nullptr,
|
|
196
|
+
const_cast<char*>("Whether the locale is bogus"), nullptr},
|
|
197
|
+
{const_cast<char*>("language"), reinterpret_cast<getter>(Locale_get_language), nullptr,
|
|
198
|
+
const_cast<char*>("The locale's ISO-639 language code"), nullptr},
|
|
199
|
+
{const_cast<char*>("country"), reinterpret_cast<getter>(Locale_get_country), nullptr,
|
|
200
|
+
const_cast<char*>("The locale's ISO-3166 country code"), nullptr},
|
|
201
|
+
{const_cast<char*>("variant"), reinterpret_cast<getter>(Locale_get_variant), nullptr,
|
|
202
|
+
const_cast<char*>("The locale's variant code"), nullptr},
|
|
203
|
+
{const_cast<char*>("extensions"), reinterpret_cast<getter>(Locale_get_extensions), nullptr,
|
|
204
|
+
const_cast<char*>("The locale's keywords and values"), nullptr},
|
|
205
|
+
{nullptr, nullptr, nullptr, nullptr, nullptr}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
PyType_Slot Locale_slots[] = {
|
|
209
|
+
{Py_tp_doc, const_cast<char*>("ICU Locale")},
|
|
210
|
+
{Py_tp_dealloc, reinterpret_cast<void*>(Locale_dealloc)},
|
|
211
|
+
{Py_tp_init, reinterpret_cast<void*>(Locale_init)},
|
|
212
|
+
{Py_tp_new, reinterpret_cast<void*>(Locale_new)},
|
|
213
|
+
{Py_tp_getset, Locale_getsetters},
|
|
214
|
+
{0, nullptr}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
PyType_Spec Locale_spec = {
|
|
218
|
+
"icu4py.locale.Locale",
|
|
219
|
+
sizeof(LocaleObject),
|
|
220
|
+
0,
|
|
221
|
+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
222
|
+
Locale_slots
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
PyMethodDef locale_module_methods[] = {
|
|
226
|
+
{nullptr, nullptr, 0, nullptr}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
int icu4py_locale_exec(PyObject* m) {
|
|
230
|
+
PyObject* type_obj = PyType_FromModuleAndSpec(m, &Locale_spec, nullptr);
|
|
231
|
+
if (type_obj == nullptr) {
|
|
232
|
+
return -1;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (PyModule_AddObject(m, "Locale", type_obj) < 0) {
|
|
236
|
+
Py_DECREF(type_obj);
|
|
237
|
+
return -1;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
PyModuleDef_Slot locale_slots[] = {
|
|
244
|
+
{Py_mod_exec, reinterpret_cast<void*>(icu4py_locale_exec)},
|
|
245
|
+
#ifdef Py_GIL_DISABLED
|
|
246
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
247
|
+
#endif
|
|
248
|
+
{0, nullptr}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
static PyModuleDef localemodule = {
|
|
252
|
+
PyModuleDef_HEAD_INIT,
|
|
253
|
+
"icu4py.locale",
|
|
254
|
+
"",
|
|
255
|
+
0,
|
|
256
|
+
locale_module_methods,
|
|
257
|
+
locale_slots,
|
|
258
|
+
nullptr,
|
|
259
|
+
nullptr,
|
|
260
|
+
nullptr,
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
} // anonymous namespace
|
|
264
|
+
|
|
265
|
+
PyMODINIT_FUNC PyInit_locale() {
|
|
266
|
+
return PyModuleDef_Init(&localemodule);
|
|
267
|
+
}
|
|
Binary file
|
icu4py/locale.pyi
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import overload
|
|
2
|
+
|
|
3
|
+
from typing_extensions import disjoint_base
|
|
4
|
+
|
|
5
|
+
@disjoint_base
|
|
6
|
+
class Locale:
|
|
7
|
+
@overload
|
|
8
|
+
def __init__(self, language: str) -> None: ...
|
|
9
|
+
@overload
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
language: str,
|
|
13
|
+
country: str,
|
|
14
|
+
variant: str | None = None,
|
|
15
|
+
extensions: dict[str, str] | None = None,
|
|
16
|
+
) -> None: ...
|
|
17
|
+
@property
|
|
18
|
+
def bogus(self) -> bool: ...
|
|
19
|
+
@property
|
|
20
|
+
def language(self) -> str: ...
|
|
21
|
+
@property
|
|
22
|
+
def country(self) -> str: ...
|
|
23
|
+
@property
|
|
24
|
+
def variant(self) -> str: ...
|
|
25
|
+
@property
|
|
26
|
+
def extensions(self) -> dict[str, str]: ...
|
icu4py/messageformat.cpp
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
#define PY_SSIZE_T_CLEAN
|
|
2
|
+
#include <Python.h>
|
|
3
|
+
#include <unicode/msgfmt.h>
|
|
4
|
+
#include <unicode/unistr.h>
|
|
5
|
+
#include <unicode/fmtable.h>
|
|
6
|
+
#include <unicode/locid.h>
|
|
7
|
+
#include <unicode/parsepos.h>
|
|
8
|
+
#include <unicode/ustring.h>
|
|
9
|
+
#include <unicode/utypes.h>
|
|
10
|
+
|
|
11
|
+
#include <cstring>
|
|
12
|
+
#include <memory>
|
|
13
|
+
|
|
14
|
+
#include "locale_types.h"
|
|
15
|
+
|
|
16
|
+
namespace {
|
|
17
|
+
|
|
18
|
+
using icu::MessageFormat;
|
|
19
|
+
using icu::UnicodeString;
|
|
20
|
+
using icu::Formattable;
|
|
21
|
+
using icu::Locale;
|
|
22
|
+
using icu::FieldPosition;
|
|
23
|
+
using icu::StringPiece;
|
|
24
|
+
using icu4py::LocaleObject;
|
|
25
|
+
|
|
26
|
+
struct ModuleState {
|
|
27
|
+
PyObject* datetime_datetime_type;
|
|
28
|
+
PyObject* datetime_date_type;
|
|
29
|
+
PyObject* datetime_time_type;
|
|
30
|
+
PyObject* decimal_decimal_type;
|
|
31
|
+
PyObject* locale_type;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
static inline ModuleState* get_module_state(PyObject* module) {
|
|
35
|
+
void* state = PyModule_GetState(module);
|
|
36
|
+
return static_cast<ModuleState*>(state);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
int icu4py_messageformat_exec(PyObject* m);
|
|
40
|
+
int icu4py_messageformat_traverse(PyObject* m, visitproc visit, void* arg);
|
|
41
|
+
int icu4py_messageformat_clear(PyObject* m);
|
|
42
|
+
|
|
43
|
+
PyMethodDef icu4py_messageformat_module_methods[] = {
|
|
44
|
+
{nullptr, nullptr, 0, nullptr}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
PyModuleDef_Slot icu4py_messageformat_slots[] = {
|
|
48
|
+
{Py_mod_exec, reinterpret_cast<void*>(icu4py_messageformat_exec)},
|
|
49
|
+
// On Python 3.13+, declare free-threaded support.
|
|
50
|
+
// https://py-free-threading.github.io/porting-extensions/#declaring-free-threaded-support
|
|
51
|
+
#ifdef Py_GIL_DISABLED
|
|
52
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
53
|
+
#endif
|
|
54
|
+
{0, nullptr}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
static PyModuleDef icu4pymodule = {
|
|
58
|
+
PyModuleDef_HEAD_INIT,
|
|
59
|
+
"icu4py.messageformat",
|
|
60
|
+
"",
|
|
61
|
+
sizeof(ModuleState),
|
|
62
|
+
icu4py_messageformat_module_methods,
|
|
63
|
+
icu4py_messageformat_slots,
|
|
64
|
+
icu4py_messageformat_traverse,
|
|
65
|
+
icu4py_messageformat_clear,
|
|
66
|
+
nullptr,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
struct MessageFormatObject {
|
|
70
|
+
PyObject_HEAD
|
|
71
|
+
MessageFormat* formatter;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
void MessageFormat_dealloc(MessageFormatObject* self) {
|
|
75
|
+
delete self->formatter;
|
|
76
|
+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
PyObject* MessageFormat_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
|
|
80
|
+
auto* self = reinterpret_cast<MessageFormatObject*>(type->tp_alloc(type, 0));
|
|
81
|
+
if (self != nullptr) {
|
|
82
|
+
self->formatter = nullptr;
|
|
83
|
+
}
|
|
84
|
+
return reinterpret_cast<PyObject*>(self);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
int MessageFormat_init(MessageFormatObject* self, PyObject* args, PyObject* kwds) {
|
|
88
|
+
const char* pattern;
|
|
89
|
+
PyObject* locale_obj;
|
|
90
|
+
Py_ssize_t pattern_len;
|
|
91
|
+
|
|
92
|
+
static const char* kwlist[] = {"pattern", "locale", nullptr};
|
|
93
|
+
|
|
94
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#O",
|
|
95
|
+
const_cast<char**>(kwlist),
|
|
96
|
+
&pattern, &pattern_len, &locale_obj)) {
|
|
97
|
+
return -1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#if PY_VERSION_HEX < 0x030B0000
|
|
101
|
+
PyObject* module = _PyType_GetModuleByDef(Py_TYPE(self), &icu4pymodule);
|
|
102
|
+
#else
|
|
103
|
+
PyObject* module = PyType_GetModuleByDef(Py_TYPE(self), &icu4pymodule);
|
|
104
|
+
#endif
|
|
105
|
+
if (module == nullptr) {
|
|
106
|
+
return -1;
|
|
107
|
+
}
|
|
108
|
+
ModuleState* mod_state = get_module_state(module);
|
|
109
|
+
|
|
110
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
111
|
+
UnicodeString upattern = UnicodeString::fromUTF8(StringPiece(pattern, pattern_len));
|
|
112
|
+
Locale locale;
|
|
113
|
+
|
|
114
|
+
if (PyUnicode_Check(locale_obj)) {
|
|
115
|
+
const char* locale_str = PyUnicode_AsUTF8(locale_obj);
|
|
116
|
+
if (locale_str == nullptr) {
|
|
117
|
+
return -1;
|
|
118
|
+
}
|
|
119
|
+
locale = Locale(locale_str);
|
|
120
|
+
} else {
|
|
121
|
+
int is_locale = PyObject_IsInstance(locale_obj, mod_state->locale_type);
|
|
122
|
+
if (is_locale == -1) {
|
|
123
|
+
return -1;
|
|
124
|
+
}
|
|
125
|
+
if (is_locale == 0) {
|
|
126
|
+
PyErr_SetString(PyExc_TypeError, "locale must be a string or Locale object");
|
|
127
|
+
return -1;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
LocaleObject* locale_pyobj = reinterpret_cast<LocaleObject*>(locale_obj);
|
|
131
|
+
if (locale_pyobj->locale == nullptr) {
|
|
132
|
+
PyErr_SetString(PyExc_ValueError, "Locale object has null internal locale");
|
|
133
|
+
return -1;
|
|
134
|
+
}
|
|
135
|
+
locale = *locale_pyobj->locale;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
self->formatter = new MessageFormat(upattern, locale, status);
|
|
139
|
+
|
|
140
|
+
if (U_FAILURE(status)) {
|
|
141
|
+
delete self->formatter;
|
|
142
|
+
self->formatter = nullptr;
|
|
143
|
+
PyErr_Format(PyExc_ValueError, "Failed to create MessageFormat: %s",
|
|
144
|
+
u_errorName(status));
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
bool pyobject_to_formattable(PyObject* obj, Formattable& formattable, ModuleState* state) {
|
|
152
|
+
if (PyLong_Check(obj)) {
|
|
153
|
+
int overflow;
|
|
154
|
+
long long long_val = PyLong_AsLongLongAndOverflow(obj, &overflow);
|
|
155
|
+
if (overflow != 0) {
|
|
156
|
+
PyObject* str_obj = PyObject_Str(obj);
|
|
157
|
+
if (str_obj == nullptr) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
Py_ssize_t size;
|
|
161
|
+
const char* str_val = PyUnicode_AsUTF8AndSize(str_obj, &size);
|
|
162
|
+
if (str_val == nullptr) {
|
|
163
|
+
Py_DECREF(str_obj);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
167
|
+
formattable = Formattable(StringPiece(str_val, size), status);
|
|
168
|
+
Py_DECREF(str_obj);
|
|
169
|
+
if (U_FAILURE(status)) {
|
|
170
|
+
PyErr_Format(PyExc_ValueError, "Failed to create Formattable from overflowed int: %s",
|
|
171
|
+
u_errorName(status));
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
if (long_val == -1 && PyErr_Occurred()) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
formattable = Formattable(static_cast<int64_t>(long_val));
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (PyUnicode_Check(obj)) {
|
|
184
|
+
Py_ssize_t size;
|
|
185
|
+
const char* str_val = PyUnicode_AsUTF8AndSize(obj, &size);
|
|
186
|
+
if (str_val == nullptr) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
formattable = Formattable(UnicodeString::fromUTF8(StringPiece(str_val, size)));
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (PyFloat_Check(obj)) {
|
|
194
|
+
double dbl_val = PyFloat_AsDouble(obj);
|
|
195
|
+
if (dbl_val == -1.0 && PyErr_Occurred()) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
formattable = Formattable(dbl_val);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
int is_decimal = PyObject_IsInstance(obj, state->decimal_decimal_type);
|
|
203
|
+
if (is_decimal == -1) {
|
|
204
|
+
return false;
|
|
205
|
+
} else if (is_decimal == 1) {
|
|
206
|
+
PyObject* str_obj = PyObject_Str(obj);
|
|
207
|
+
if (str_obj == nullptr) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
Py_ssize_t size;
|
|
211
|
+
const char* str_val = PyUnicode_AsUTF8AndSize(str_obj, &size);
|
|
212
|
+
if (str_val == nullptr) {
|
|
213
|
+
Py_DECREF(str_obj);
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
217
|
+
formattable = Formattable(StringPiece(str_val, size), status);
|
|
218
|
+
Py_DECREF(str_obj);
|
|
219
|
+
if (U_FAILURE(status)) {
|
|
220
|
+
PyErr_Format(PyExc_ValueError, "Failed to create Formattable from Decimal: %s",
|
|
221
|
+
u_errorName(status));
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
int is_datetime = PyObject_IsInstance(obj, state->datetime_datetime_type);
|
|
228
|
+
if (is_datetime == -1) {
|
|
229
|
+
return false;
|
|
230
|
+
} else if (is_datetime == 1) {
|
|
231
|
+
PyObject* timestamp = PyObject_CallMethod(obj, "timestamp", nullptr);
|
|
232
|
+
if (timestamp == nullptr) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
double timestamp_seconds = PyFloat_AsDouble(timestamp);
|
|
236
|
+
Py_DECREF(timestamp);
|
|
237
|
+
if (timestamp_seconds == -1.0 && PyErr_Occurred()) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
UDate udate = timestamp_seconds * 1000.0;
|
|
241
|
+
formattable = Formattable(udate, Formattable::kIsDate);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
int is_date = PyObject_IsInstance(obj, state->datetime_date_type);
|
|
246
|
+
if (is_date == -1) {
|
|
247
|
+
return false;
|
|
248
|
+
} else if (is_date == 1) {
|
|
249
|
+
PyObject* combine = PyObject_GetAttrString(state->datetime_datetime_type, "combine");
|
|
250
|
+
if (combine == nullptr) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
PyObject* min_time = PyObject_GetAttrString(state->datetime_time_type, "min");
|
|
255
|
+
if (min_time == nullptr) {
|
|
256
|
+
Py_DECREF(combine);
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
PyObject* dt = PyObject_CallFunctionObjArgs(combine, obj, min_time, nullptr);
|
|
261
|
+
Py_DECREF(combine);
|
|
262
|
+
Py_DECREF(min_time);
|
|
263
|
+
if (dt == nullptr) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
PyObject* timestamp = PyObject_CallMethod(dt, "timestamp", nullptr);
|
|
268
|
+
Py_DECREF(dt);
|
|
269
|
+
if (timestamp == nullptr) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
double timestamp_seconds = PyFloat_AsDouble(timestamp);
|
|
273
|
+
Py_DECREF(timestamp);
|
|
274
|
+
if (timestamp_seconds == -1.0 && PyErr_Occurred()) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
UDate udate = timestamp_seconds * 1000.0;
|
|
278
|
+
formattable = Formattable(udate, Formattable::kIsDate);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
PyErr_SetString(PyExc_TypeError, "Parameter values must be int, float, str, Decimal, datetime, or date");
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
bool dict_to_parallel_arrays(PyObject* dict, ModuleState* mod_state, UnicodeString*& names,
|
|
287
|
+
Formattable*& values, int32_t& count) {
|
|
288
|
+
if (!PyDict_Check(dict)) {
|
|
289
|
+
PyErr_SetString(PyExc_TypeError, "Argument must be a dictionary");
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
count = static_cast<int32_t>(PyDict_Size(dict));
|
|
294
|
+
if (count == 0) {
|
|
295
|
+
names = nullptr;
|
|
296
|
+
values = nullptr;
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
auto names_ptr = std::make_unique<UnicodeString[]>(count);
|
|
301
|
+
auto values_ptr = std::make_unique<Formattable[]>(count);
|
|
302
|
+
|
|
303
|
+
Py_ssize_t pos = 0;
|
|
304
|
+
PyObject* key;
|
|
305
|
+
PyObject* value;
|
|
306
|
+
int32_t i = 0;
|
|
307
|
+
bool err = false;
|
|
308
|
+
|
|
309
|
+
#ifdef Py_GIL_DISABLED
|
|
310
|
+
Py_BEGIN_CRITICAL_SECTION(dict);
|
|
311
|
+
#endif
|
|
312
|
+
while (PyDict_Next(dict, &pos, &key, &value)) {
|
|
313
|
+
// Ensure we don't exceed allocated space
|
|
314
|
+
if (i >= count) {
|
|
315
|
+
PyErr_SetString(PyExc_RuntimeError, "Dictionary size changed during iteration");
|
|
316
|
+
err = true;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (key == nullptr) {
|
|
321
|
+
PyErr_SetString(PyExc_TypeError, "NULL key in dictionary");
|
|
322
|
+
err = true;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
if (value == nullptr) {
|
|
326
|
+
PyErr_SetString(PyExc_TypeError, "NULL value in dictionary");
|
|
327
|
+
err = true;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
Py_ssize_t key_size;
|
|
332
|
+
const char* key_str = PyUnicode_AsUTF8AndSize(key, &key_size);
|
|
333
|
+
if (key_str == nullptr) {
|
|
334
|
+
PyErr_SetString(PyExc_TypeError, "Dictionary keys must be strings");
|
|
335
|
+
err = true;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
names_ptr[i] = UnicodeString::fromUTF8(StringPiece(key_str, key_size));
|
|
339
|
+
|
|
340
|
+
if (!pyobject_to_formattable(value, values_ptr[i], mod_state)) {
|
|
341
|
+
if (!PyErr_Occurred()) {
|
|
342
|
+
PyErr_SetString(PyExc_TypeError, "Failed to convert dictionary value to Formattable");
|
|
343
|
+
}
|
|
344
|
+
err = true;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
++i;
|
|
348
|
+
}
|
|
349
|
+
#ifdef Py_GIL_DISABLED
|
|
350
|
+
Py_END_CRITICAL_SECTION();
|
|
351
|
+
#endif
|
|
352
|
+
if (err) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
names = names_ptr.release();
|
|
357
|
+
values = values_ptr.release();
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
PyObject* MessageFormat_format(MessageFormatObject* self, PyObject* args) {
|
|
362
|
+
PyObject* params_dict;
|
|
363
|
+
|
|
364
|
+
if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, ¶ms_dict)) {
|
|
365
|
+
return nullptr;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#if PY_VERSION_HEX < 0x030B0000
|
|
369
|
+
PyObject* module = _PyType_GetModuleByDef(Py_TYPE(self), &icu4pymodule);
|
|
370
|
+
#else
|
|
371
|
+
PyObject* module = PyType_GetModuleByDef(Py_TYPE(self), &icu4pymodule);
|
|
372
|
+
#endif
|
|
373
|
+
if (module == nullptr) {
|
|
374
|
+
return nullptr;
|
|
375
|
+
}
|
|
376
|
+
ModuleState* mod_state = get_module_state(module);
|
|
377
|
+
|
|
378
|
+
UnicodeString* argumentNames = nullptr;
|
|
379
|
+
Formattable* arguments = nullptr;
|
|
380
|
+
int32_t count = 0;
|
|
381
|
+
|
|
382
|
+
if (!dict_to_parallel_arrays(params_dict, mod_state, argumentNames, arguments, count)) {
|
|
383
|
+
return nullptr;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
auto names_guard = std::unique_ptr<UnicodeString[]>(argumentNames);
|
|
387
|
+
auto values_guard = std::unique_ptr<Formattable[]>(arguments);
|
|
388
|
+
|
|
389
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
390
|
+
UnicodeString result;
|
|
391
|
+
|
|
392
|
+
// ICU objects need external synchronization
|
|
393
|
+
#ifdef Py_GIL_DISABLED
|
|
394
|
+
Py_BEGIN_CRITICAL_SECTION(self);
|
|
395
|
+
#endif
|
|
396
|
+
|
|
397
|
+
if (count == 0) {
|
|
398
|
+
FieldPosition field_pos;
|
|
399
|
+
result = self->formatter->format(nullptr, 0, result, field_pos, status);
|
|
400
|
+
} else {
|
|
401
|
+
result = self->formatter->format(argumentNames, arguments, count, result, status);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
#ifdef Py_GIL_DISABLED
|
|
405
|
+
Py_END_CRITICAL_SECTION();
|
|
406
|
+
#endif
|
|
407
|
+
|
|
408
|
+
if (U_FAILURE(status)) {
|
|
409
|
+
PyErr_Format(PyExc_RuntimeError, "Failed to format message: %s",
|
|
410
|
+
u_errorName(status));
|
|
411
|
+
return nullptr;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
std::string utf8;
|
|
415
|
+
result.toUTF8String(utf8);
|
|
416
|
+
return PyUnicode_FromStringAndSize(utf8.c_str(), utf8.size());
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
PyMethodDef MessageFormat_methods[] = {
|
|
420
|
+
{"format", reinterpret_cast<PyCFunction>(MessageFormat_format), METH_VARARGS,
|
|
421
|
+
"Format the message with given parameters"},
|
|
422
|
+
{nullptr, nullptr, 0, nullptr}
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
PyType_Slot MessageFormat_slots[] = {
|
|
426
|
+
{Py_tp_doc, const_cast<char*>("ICU MessageFormat")},
|
|
427
|
+
{Py_tp_dealloc, reinterpret_cast<void*>(MessageFormat_dealloc)},
|
|
428
|
+
{Py_tp_init, reinterpret_cast<void*>(MessageFormat_init)},
|
|
429
|
+
{Py_tp_new, reinterpret_cast<void*>(MessageFormat_new)},
|
|
430
|
+
{Py_tp_methods, MessageFormat_methods},
|
|
431
|
+
{0, nullptr}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
PyType_Spec MessageFormat_spec = {
|
|
435
|
+
"icu4py.messageformat.MessageFormat",
|
|
436
|
+
sizeof(MessageFormatObject),
|
|
437
|
+
0,
|
|
438
|
+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
439
|
+
MessageFormat_slots
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
int icu4py_messageformat_exec(PyObject* m) {
|
|
443
|
+
PyObject* type_obj = PyType_FromModuleAndSpec(m, &MessageFormat_spec, nullptr);
|
|
444
|
+
if (type_obj == nullptr) {
|
|
445
|
+
return -1;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (PyModule_AddObject(m, "MessageFormat", type_obj) < 0) {
|
|
449
|
+
Py_DECREF(type_obj);
|
|
450
|
+
return -1;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
ModuleState* state = get_module_state(m);
|
|
454
|
+
|
|
455
|
+
PyObject* datetime_module = PyImport_ImportModule("datetime");
|
|
456
|
+
if (datetime_module == nullptr) {
|
|
457
|
+
return -1;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
state->datetime_datetime_type = PyObject_GetAttrString(datetime_module, "datetime");
|
|
461
|
+
state->datetime_date_type = PyObject_GetAttrString(datetime_module, "date");
|
|
462
|
+
state->datetime_time_type = PyObject_GetAttrString(datetime_module, "time");
|
|
463
|
+
Py_DECREF(datetime_module);
|
|
464
|
+
|
|
465
|
+
if (state->datetime_datetime_type == nullptr ||
|
|
466
|
+
state->datetime_date_type == nullptr ||
|
|
467
|
+
state->datetime_time_type == nullptr) {
|
|
468
|
+
return -1;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
PyObject* decimal_module = PyImport_ImportModule("decimal");
|
|
472
|
+
if (decimal_module == nullptr) {
|
|
473
|
+
return -1;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
state->decimal_decimal_type = PyObject_GetAttrString(decimal_module, "Decimal");
|
|
477
|
+
Py_DECREF(decimal_module);
|
|
478
|
+
|
|
479
|
+
if (state->decimal_decimal_type == nullptr) {
|
|
480
|
+
return -1;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
PyObject* locale_module = PyImport_ImportModule("icu4py.locale");
|
|
484
|
+
if (locale_module == nullptr) {
|
|
485
|
+
return -1;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
state->locale_type = PyObject_GetAttrString(locale_module, "Locale");
|
|
489
|
+
Py_DECREF(locale_module);
|
|
490
|
+
|
|
491
|
+
if (state->locale_type == nullptr) {
|
|
492
|
+
return -1;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return 0;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
int icu4py_messageformat_traverse(PyObject* m, visitproc visit, void* arg) {
|
|
499
|
+
ModuleState* state = get_module_state(m);
|
|
500
|
+
Py_VISIT(state->datetime_datetime_type);
|
|
501
|
+
Py_VISIT(state->datetime_date_type);
|
|
502
|
+
Py_VISIT(state->datetime_time_type);
|
|
503
|
+
Py_VISIT(state->decimal_decimal_type);
|
|
504
|
+
Py_VISIT(state->locale_type);
|
|
505
|
+
return 0;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
int icu4py_messageformat_clear(PyObject* m) {
|
|
509
|
+
ModuleState* state = get_module_state(m);
|
|
510
|
+
Py_CLEAR(state->datetime_datetime_type);
|
|
511
|
+
Py_CLEAR(state->datetime_date_type);
|
|
512
|
+
Py_CLEAR(state->datetime_time_type);
|
|
513
|
+
Py_CLEAR(state->decimal_decimal_type);
|
|
514
|
+
Py_CLEAR(state->locale_type);
|
|
515
|
+
return 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
} // anonymous namespace
|
|
519
|
+
|
|
520
|
+
PyMODINIT_FUNC PyInit_messageformat() {
|
|
521
|
+
return PyModuleDef_Init(&icu4pymodule);
|
|
522
|
+
}
|
|
Binary file
|
icu4py/messageformat.pyi
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from typing_extensions import disjoint_base
|
|
5
|
+
|
|
6
|
+
from icu4py.locale import Locale
|
|
7
|
+
|
|
8
|
+
@disjoint_base
|
|
9
|
+
class MessageFormat:
|
|
10
|
+
def __init__(self, pattern: str, locale: str | Locale) -> None: ...
|
|
11
|
+
def format(
|
|
12
|
+
self, params: dict[str, int | float | str | Decimal | date | datetime]
|
|
13
|
+
) -> str: ...
|
icu4py/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: icu4py
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Bindings to the ICU (International Components for Unicode) library (ICU4C).
|
|
5
|
+
Author-email: Adam Johnson <me@adamj.eu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Changelog, https://icu4py.readthedocs.io/en/latest/changelog.html
|
|
8
|
+
Project-URL: Documentation, https://icu4py.readthedocs.io/
|
|
9
|
+
Project-URL: Funding, https://adamj.eu/books/
|
|
10
|
+
Project-URL: Repository, https://github.com/adamchainz/icu4py
|
|
11
|
+
Keywords: i18n,icu,icu4c,internationalization,l10n,localization,unicode
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Framework :: Pytest
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/x-rst
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: typing-extensions>=4.15
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
======
|
|
31
|
+
icu4py
|
|
32
|
+
======
|
|
33
|
+
|
|
34
|
+
.. image:: https://img.shields.io/readthedocs/icu4py?style=for-the-badge
|
|
35
|
+
:target: https://icu4py.readthedocs.io/en/latest/
|
|
36
|
+
|
|
37
|
+
.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/icu4py/main.yml.svg?branch=main&style=for-the-badge
|
|
38
|
+
:target: https://github.com/adamchainz/icu4py/actions?workflow=CI
|
|
39
|
+
|
|
40
|
+
.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
|
|
41
|
+
:target: https://github.com/adamchainz/icu4py/actions?workflow=CI
|
|
42
|
+
|
|
43
|
+
.. image:: https://img.shields.io/pypi/v/icu4py.svg?style=for-the-badge
|
|
44
|
+
:target: https://pypi.org/project/icu4py/
|
|
45
|
+
|
|
46
|
+
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
|
|
47
|
+
:target: https://github.com/psf/black
|
|
48
|
+
|
|
49
|
+
.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
|
|
50
|
+
:target: https://github.com/pre-commit/pre-commit
|
|
51
|
+
:alt: pre-commit
|
|
52
|
+
|
|
53
|
+
----
|
|
54
|
+
|
|
55
|
+
Bindings to the ICU (International Components for Unicode) library (ICU4C).
|
|
56
|
+
|
|
57
|
+
Documentation
|
|
58
|
+
=============
|
|
59
|
+
|
|
60
|
+
Please see https://icu4py.readthedocs.io/.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
icu4py/__init__.py,sha256=yd6ls9f_x8oeU3r7IckxKbisUmRm248CXOhAIYiUN1o,603
|
|
2
|
+
icu4py/_version.cpp,sha256=idstiuGpfe-qXcv0tiAWOAUpSmDKsPzqB3sQDVu-cTc,1198
|
|
3
|
+
icu4py/_version.cpython-312-aarch64-linux-musl.so,sha256=RAEKuycOrAIkHTkpQieAdOMCFg9cl0rGaX8j_jFvgig,197457
|
|
4
|
+
icu4py/_version.pyi,sha256=4Y8qCvpvU9n5aYm4a91lMh4KKoxe1zl-i-RD6mnAYiY,97
|
|
5
|
+
icu4py/locale.cpp,sha256=Utkys6iRn2cyzySGEwtA3naXou59Bw5Yp5lpIr3rdYs,7522
|
|
6
|
+
icu4py/locale.cpython-312-aarch64-linux-musl.so,sha256=PV7g6OFH3gwrf7c3jF7anMv_yIweBQZX3kAepqhHG38,263809
|
|
7
|
+
icu4py/locale.pyi,sha256=em0XwMnghSxJzvYBbP-5Z9eei9SORkCMIJ95FciyyL8,618
|
|
8
|
+
icu4py/messageformat.cpp,sha256=J8VDS81sDUdRooNao6pQjc2WPx91XuqO358QoVhwlzQ,16065
|
|
9
|
+
icu4py/messageformat.cpython-312-aarch64-linux-musl.so,sha256=xLMiDPe8Y67tkybO3Z3ZUDJxO4BFELgHB6lIvedQJcU,333665
|
|
10
|
+
icu4py/messageformat.pyi,sha256=CXBlWn45WKsj9mJuPyCNifTKzqF56_taMgqpDhXrEkk,365
|
|
11
|
+
icu4py/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
icu4py.libs/libgcc_s-2d945d6c.so.1,sha256=dn-5kQf6XkEWhBOx7tnXMZHJQSbDdSYmPhrx8fiYeI8,201673
|
|
13
|
+
icu4py.libs/libicudata-3b40cc52.so.76.1,sha256=kQ8uk8VWWqZy8jJ5Tj_-kUQDSf3RGY9nRwO0aPPjZdw,131265
|
|
14
|
+
icu4py.libs/libicui18n-3545b8ef.so.76.1,sha256=U9qa43a2Yg06oQrtNqCkfHFJLoLPwjbLM5-EcZDTFD0,4455993
|
|
15
|
+
icu4py.libs/libicuuc-130fd364.so.76.1,sha256=cyerdaVD7tLdXYtCGU-o5cZmSxq9cpTr-UbjAzQwb14,2335249
|
|
16
|
+
icu4py.libs/libstdc++-85f2cd6d.so.6.0.33,sha256=VjT6H3rGbIUp02akWf5sJoz8LifUZ4OFZJYGOR_KyeI,3650097
|
|
17
|
+
icu4py-0.2.0.dist-info/METADATA,sha256=1Jc-hWpxxXk-8pAcWnC3irtwxHD-M_kDBEKByNFp5gE,2325
|
|
18
|
+
icu4py-0.2.0.dist-info/WHEEL,sha256=OhR_OrUHeSS9Wv6z5202HxeJVPkI7JBt4nUD2fey9No,113
|
|
19
|
+
icu4py-0.2.0.dist-info/top_level.txt,sha256=bT8eoOy2eVL3nNv2IXqQo-KpPtbBoR415Kow3HGtId8,7
|
|
20
|
+
icu4py-0.2.0.dist-info/RECORD,,
|
|
21
|
+
icu4py-0.2.0.dist-info/licenses/LICENSE,sha256=blVuFA7Qdp2_CcTyGXLxA4CsHsAjAquxVc4O6ODqye4,1069
|
|
22
|
+
icu4py-0.2.0.dist-info/sboms/auditwheel.cdx.json,sha256=hGjsB7aoov7QCllE_IYwTkK0jJk5n0PVk9dpf2xXuUs,2949
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Adam Johnson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"bomFormat": "CycloneDX", "specVersion": "1.4", "version": 1, "metadata": {"component": {"type": "library", "bom-ref": "pkg:pypi/icu4py@0.2.0?file_name=icu4py-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", "name": "icu4py", "version": "0.2.0", "purl": "pkg:pypi/icu4py@0.2.0?file_name=icu4py-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl"}, "tools": [{"name": "auditwheel", "version": "6.6.0"}]}, "components": [{"type": "library", "bom-ref": "pkg:pypi/icu4py@0.2.0?file_name=icu4py-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", "name": "icu4py", "version": "0.2.0", "purl": "pkg:pypi/icu4py@0.2.0?file_name=icu4py-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl"}, {"type": "library", "bom-ref": "pkg:apk/alpine/icu@libs-76.1-r1#2b29a8479facfe8ebdf2de98cce4e1cc307504d22af217d23374c9c476f17120", "name": "icu", "version": "libs-76.1-r1", "purl": "pkg:apk/alpine/icu@libs-76.1-r1"}, {"type": "library", "bom-ref": "pkg:apk/alpine/libgcc@14.2.0-r6#933a623c9e323e83b1734e630a342f206999adc096732a3fea9896fa0181ea29", "name": "libgcc", "version": "14.2.0-r6", "purl": "pkg:apk/alpine/libgcc@14.2.0-r6"}, {"type": "library", "bom-ref": "pkg:apk/alpine/libstdc%2B%2B@14.2.0-r6#76f023cbc3d7b369d6008f354e88aa983d13e4bd8f06e4e49a01686039fe1509", "name": "libstdc++", "version": "14.2.0-r6", "purl": "pkg:apk/alpine/libstdc%2B%2B@14.2.0-r6"}, {"type": "library", "bom-ref": "pkg:apk/alpine/icu@libs-76.1-r1#8d6a71c39a07856c284fa5b39a13d7cbdb2bdd060a0ec15e96630ecddff28b4e", "name": "icu", "version": "libs-76.1-r1", "purl": "pkg:apk/alpine/icu@libs-76.1-r1"}, {"type": "library", "bom-ref": "pkg:apk/alpine/icu@libs-76.1-r1#7df0d52a2b60f1c86ef8c088a6b8ac4d1d0b957c989656b70d84add7e336e477", "name": "icu", "version": "libs-76.1-r1", "purl": "pkg:apk/alpine/icu@libs-76.1-r1"}], "dependencies": [{"ref": "pkg:pypi/icu4py@0.2.0?file_name=icu4py-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", "dependsOn": ["pkg:apk/alpine/icu@libs-76.1-r1#2b29a8479facfe8ebdf2de98cce4e1cc307504d22af217d23374c9c476f17120", "pkg:apk/alpine/libgcc@14.2.0-r6#933a623c9e323e83b1734e630a342f206999adc096732a3fea9896fa0181ea29", "pkg:apk/alpine/libstdc%2B%2B@14.2.0-r6#76f023cbc3d7b369d6008f354e88aa983d13e4bd8f06e4e49a01686039fe1509", "pkg:apk/alpine/icu@libs-76.1-r1#8d6a71c39a07856c284fa5b39a13d7cbdb2bdd060a0ec15e96630ecddff28b4e", "pkg:apk/alpine/icu@libs-76.1-r1#7df0d52a2b60f1c86ef8c088a6b8ac4d1d0b957c989656b70d84add7e336e477"]}, {"ref": "pkg:apk/alpine/icu@libs-76.1-r1#2b29a8479facfe8ebdf2de98cce4e1cc307504d22af217d23374c9c476f17120"}, {"ref": "pkg:apk/alpine/libgcc@14.2.0-r6#933a623c9e323e83b1734e630a342f206999adc096732a3fea9896fa0181ea29"}, {"ref": "pkg:apk/alpine/libstdc%2B%2B@14.2.0-r6#76f023cbc3d7b369d6008f354e88aa983d13e4bd8f06e4e49a01686039fe1509"}, {"ref": "pkg:apk/alpine/icu@libs-76.1-r1#8d6a71c39a07856c284fa5b39a13d7cbdb2bdd060a0ec15e96630ecddff28b4e"}, {"ref": "pkg:apk/alpine/icu@libs-76.1-r1#7df0d52a2b60f1c86ef8c088a6b8ac4d1d0b957c989656b70d84add7e336e477"}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
icu4py
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|