icu4py 0.1.0__cp314-cp314-macosx_15_0_x86_64.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/.dylibs/libicudata.78.1.dylib +0 -0
- icu4py/.dylibs/libicui18n.78.1.dylib +0 -0
- icu4py/.dylibs/libicuuc.78.1.dylib +0 -0
- icu4py/__init__.py +0 -0
- icu4py/messageformat.cpp +295 -0
- icu4py/messageformat.cpython-314-darwin.so +0 -0
- icu4py/messageformat.pyi +6 -0
- icu4py/py.typed +0 -0
- icu4py-0.1.0.dist-info/METADATA +59 -0
- icu4py-0.1.0.dist-info/RECORD +13 -0
- icu4py-0.1.0.dist-info/WHEEL +6 -0
- icu4py-0.1.0.dist-info/licenses/LICENSE +21 -0
- icu4py-0.1.0.dist-info/top_level.txt +1 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
icu4py/__init__.py
ADDED
|
File without changes
|
icu4py/messageformat.cpp
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
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
|
+
|
|
10
|
+
#include <cstring>
|
|
11
|
+
#include <memory>
|
|
12
|
+
|
|
13
|
+
namespace {
|
|
14
|
+
|
|
15
|
+
using icu::MessageFormat;
|
|
16
|
+
using icu::UnicodeString;
|
|
17
|
+
using icu::Formattable;
|
|
18
|
+
using icu::Locale;
|
|
19
|
+
using icu::FieldPosition;
|
|
20
|
+
using icu::StringPiece;
|
|
21
|
+
|
|
22
|
+
struct MessageFormatObject {
|
|
23
|
+
PyObject_HEAD
|
|
24
|
+
MessageFormat* formatter;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
void MessageFormat_dealloc(MessageFormatObject* self) {
|
|
28
|
+
delete self->formatter;
|
|
29
|
+
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
PyObject* MessageFormat_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
|
|
33
|
+
auto* self = reinterpret_cast<MessageFormatObject*>(type->tp_alloc(type, 0));
|
|
34
|
+
if (self != nullptr) {
|
|
35
|
+
self->formatter = nullptr;
|
|
36
|
+
}
|
|
37
|
+
return reinterpret_cast<PyObject*>(self);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
int MessageFormat_init(MessageFormatObject* self, PyObject* args, PyObject* kwds) {
|
|
41
|
+
const char* pattern;
|
|
42
|
+
const char* locale_str;
|
|
43
|
+
Py_ssize_t pattern_len;
|
|
44
|
+
|
|
45
|
+
static const char* kwlist[] = {"pattern", "locale", nullptr};
|
|
46
|
+
|
|
47
|
+
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#s",
|
|
48
|
+
const_cast<char**>(kwlist),
|
|
49
|
+
&pattern, &pattern_len, &locale_str)) {
|
|
50
|
+
return -1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
54
|
+
UnicodeString upattern = UnicodeString::fromUTF8(StringPiece(pattern, pattern_len));
|
|
55
|
+
Locale locale(locale_str);
|
|
56
|
+
|
|
57
|
+
self->formatter = new MessageFormat(upattern, locale, status);
|
|
58
|
+
|
|
59
|
+
if (U_FAILURE(status)) {
|
|
60
|
+
delete self->formatter;
|
|
61
|
+
self->formatter = nullptr;
|
|
62
|
+
PyErr_Format(PyExc_ValueError, "Failed to create MessageFormat: %s",
|
|
63
|
+
u_errorName(status));
|
|
64
|
+
return -1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
bool pyobject_to_formattable(PyObject* obj, Formattable& formattable) {
|
|
71
|
+
if (PyLong_Check(obj)) {
|
|
72
|
+
long long_val = PyLong_AsLongLong(obj);
|
|
73
|
+
if (long_val == -1 && PyErr_Occurred()) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
formattable = Formattable(static_cast<int64_t>(long_val));
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (PyUnicode_Check(obj)) {
|
|
81
|
+
Py_ssize_t size;
|
|
82
|
+
const char* str_val = PyUnicode_AsUTF8AndSize(obj, &size);
|
|
83
|
+
if (str_val == nullptr) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
formattable = Formattable(UnicodeString::fromUTF8(StringPiece(str_val, size)));
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (PyFloat_Check(obj)) {
|
|
91
|
+
double dbl_val = PyFloat_AsDouble(obj);
|
|
92
|
+
formattable = Formattable(dbl_val);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
PyErr_SetString(PyExc_TypeError, "Parameter values must be int, float, or str");
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
bool dict_to_parallel_arrays(PyObject* dict, UnicodeString*& names,
|
|
101
|
+
Formattable*& values, int32_t& count) {
|
|
102
|
+
if (!PyDict_Check(dict)) {
|
|
103
|
+
PyErr_SetString(PyExc_TypeError, "Argument must be a dictionary");
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
count = static_cast<int32_t>(PyDict_Size(dict));
|
|
108
|
+
if (count == 0) {
|
|
109
|
+
names = nullptr;
|
|
110
|
+
values = nullptr;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
auto names_ptr = std::make_unique<UnicodeString[]>(count);
|
|
115
|
+
auto values_ptr = std::make_unique<Formattable[]>(count);
|
|
116
|
+
|
|
117
|
+
Py_ssize_t pos = 0;
|
|
118
|
+
PyObject* key;
|
|
119
|
+
PyObject* value;
|
|
120
|
+
int32_t i = 0;
|
|
121
|
+
bool err = false;
|
|
122
|
+
|
|
123
|
+
#ifdef Py_GIL_DISABLED
|
|
124
|
+
Py_BEGIN_CRITICAL_SECTION(dict);
|
|
125
|
+
#endif
|
|
126
|
+
while (PyDict_Next(dict, &pos, &key, &value)) {
|
|
127
|
+
// Ensure we don't exceed allocated space
|
|
128
|
+
if (i >= count) {
|
|
129
|
+
PyErr_SetString(PyExc_RuntimeError, "Dictionary size changed during iteration");
|
|
130
|
+
err = true;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (key == nullptr) {
|
|
135
|
+
PyErr_SetString(PyExc_TypeError, "NULL key in dictionary");
|
|
136
|
+
err = true;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
if (value == nullptr) {
|
|
140
|
+
PyErr_SetString(PyExc_TypeError, "NULL value in dictionary");
|
|
141
|
+
err = true;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
Py_ssize_t key_size;
|
|
146
|
+
const char* key_str = PyUnicode_AsUTF8AndSize(key, &key_size);
|
|
147
|
+
if (key_str == nullptr) {
|
|
148
|
+
PyErr_SetString(PyExc_TypeError, "Dictionary keys must be strings");
|
|
149
|
+
err = true;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
names_ptr[i] = UnicodeString::fromUTF8(StringPiece(key_str, key_size));
|
|
153
|
+
|
|
154
|
+
if (!pyobject_to_formattable(value, values_ptr[i])) {
|
|
155
|
+
PyErr_SetString(PyExc_TypeError, "Failed to convert dictionary value to Formattable");
|
|
156
|
+
err = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
++i;
|
|
160
|
+
}
|
|
161
|
+
#ifdef Py_GIL_DISABLED
|
|
162
|
+
Py_END_CRITICAL_SECTION();
|
|
163
|
+
#endif
|
|
164
|
+
if (err) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
names = names_ptr.release();
|
|
169
|
+
values = values_ptr.release();
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
PyObject* MessageFormat_format(MessageFormatObject* self, PyObject* args) {
|
|
174
|
+
PyObject* params_dict;
|
|
175
|
+
|
|
176
|
+
if (!PyArg_ParseTuple(args, "O!", &PyDict_Type, ¶ms_dict)) {
|
|
177
|
+
return nullptr;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
UnicodeString* argumentNames = nullptr;
|
|
181
|
+
Formattable* arguments = nullptr;
|
|
182
|
+
int32_t count = 0;
|
|
183
|
+
|
|
184
|
+
if (!dict_to_parallel_arrays(params_dict, argumentNames, arguments, count)) {
|
|
185
|
+
return nullptr;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
auto names_guard = std::unique_ptr<UnicodeString[]>(argumentNames);
|
|
189
|
+
auto values_guard = std::unique_ptr<Formattable[]>(arguments);
|
|
190
|
+
|
|
191
|
+
UErrorCode status = U_ZERO_ERROR;
|
|
192
|
+
UnicodeString result;
|
|
193
|
+
|
|
194
|
+
if (count == 0) {
|
|
195
|
+
FieldPosition field_pos;
|
|
196
|
+
result = self->formatter->format(nullptr, 0, result, field_pos, status);
|
|
197
|
+
} else {
|
|
198
|
+
result = self->formatter->format(argumentNames, arguments, count, result, status);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (U_FAILURE(status)) {
|
|
202
|
+
PyErr_Format(PyExc_RuntimeError, "Failed to format message: %s",
|
|
203
|
+
u_errorName(status));
|
|
204
|
+
return nullptr;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
std::string utf8;
|
|
208
|
+
result.toUTF8String(utf8);
|
|
209
|
+
return PyUnicode_FromStringAndSize(utf8.c_str(), utf8.size());
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
PyMethodDef MessageFormat_methods[] = {
|
|
213
|
+
{"format", reinterpret_cast<PyCFunction>(MessageFormat_format), METH_VARARGS,
|
|
214
|
+
"Format the message with given parameters"},
|
|
215
|
+
{nullptr, nullptr, 0, nullptr}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
PyTypeObject MessageFormatType = {
|
|
219
|
+
PyVarObject_HEAD_INIT(nullptr, 0)
|
|
220
|
+
"icu4py.messageformat.MessageFormat", /* tp_name */
|
|
221
|
+
sizeof(MessageFormatObject), /* tp_basicsize */
|
|
222
|
+
0, /* tp_itemsize */
|
|
223
|
+
reinterpret_cast<destructor>(MessageFormat_dealloc), /* tp_dealloc */
|
|
224
|
+
0, /* tp_vectorcall_offset */
|
|
225
|
+
nullptr, /* tp_getattr */
|
|
226
|
+
nullptr, /* tp_setattr */
|
|
227
|
+
nullptr, /* tp_as_async */
|
|
228
|
+
nullptr, /* tp_repr */
|
|
229
|
+
nullptr, /* tp_as_number */
|
|
230
|
+
nullptr, /* tp_as_sequence */
|
|
231
|
+
nullptr, /* tp_as_mapping */
|
|
232
|
+
nullptr, /* tp_hash */
|
|
233
|
+
nullptr, /* tp_call */
|
|
234
|
+
nullptr, /* tp_str */
|
|
235
|
+
nullptr, /* tp_getattro */
|
|
236
|
+
nullptr, /* tp_setattro */
|
|
237
|
+
nullptr, /* tp_as_buffer */
|
|
238
|
+
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
239
|
+
"ICU MessageFormat", /* tp_doc */
|
|
240
|
+
nullptr, /* tp_traverse */
|
|
241
|
+
nullptr, /* tp_clear */
|
|
242
|
+
nullptr, /* tp_richcompare */
|
|
243
|
+
0, /* tp_weaklistoffset */
|
|
244
|
+
nullptr, /* tp_iter */
|
|
245
|
+
nullptr, /* tp_iternext */
|
|
246
|
+
MessageFormat_methods, /* tp_methods */
|
|
247
|
+
nullptr, /* tp_members */
|
|
248
|
+
nullptr, /* tp_getset */
|
|
249
|
+
nullptr, /* tp_base */
|
|
250
|
+
nullptr, /* tp_dict */
|
|
251
|
+
nullptr, /* tp_descr_get */
|
|
252
|
+
nullptr, /* tp_descr_set */
|
|
253
|
+
0, /* tp_dictoffset */
|
|
254
|
+
reinterpret_cast<initproc>(MessageFormat_init), /* tp_init */
|
|
255
|
+
nullptr, /* tp_alloc */
|
|
256
|
+
MessageFormat_new, /* tp_new */
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
int icu4py_messageformat_exec(PyObject* m) {
|
|
260
|
+
if (PyType_Ready(&MessageFormatType) < 0) {
|
|
261
|
+
return -1;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
Py_INCREF(&MessageFormatType);
|
|
265
|
+
if (PyModule_AddObject(m, "MessageFormat",
|
|
266
|
+
reinterpret_cast<PyObject*>(&MessageFormatType)) < 0) {
|
|
267
|
+
Py_DECREF(&MessageFormatType);
|
|
268
|
+
return -1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
PyModuleDef_Slot icu4py_messageformat_slots[] = {
|
|
275
|
+
{Py_mod_exec, reinterpret_cast<void*>(icu4py_messageformat_exec)},
|
|
276
|
+
#ifdef Py_GIL_DISABLED
|
|
277
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
278
|
+
#endif
|
|
279
|
+
{0, nullptr}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
PyModuleDef icumodule = {
|
|
283
|
+
PyModuleDef_HEAD_INIT,
|
|
284
|
+
"icu4py.messageformat", /* m_name */
|
|
285
|
+
"", /* m_doc */
|
|
286
|
+
0, /* m_size */
|
|
287
|
+
nullptr, /* m_methods */
|
|
288
|
+
icu4py_messageformat_slots, /* m_slots */
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
} // anonymous namespace
|
|
292
|
+
|
|
293
|
+
PyMODINIT_FUNC PyInit_messageformat() {
|
|
294
|
+
return PyModuleDef_Init(&icumodule);
|
|
295
|
+
}
|
|
Binary file
|
icu4py/messageformat.pyi
ADDED
icu4py/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: icu4py
|
|
3
|
+
Version: 0.1.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
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
======
|
|
30
|
+
icu4py
|
|
31
|
+
======
|
|
32
|
+
|
|
33
|
+
.. image:: https://img.shields.io/readthedocs/icu4py?style=for-the-badge
|
|
34
|
+
:target: https://icu4py.readthedocs.io/en/latest/
|
|
35
|
+
|
|
36
|
+
.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/icu4py/main.yml.svg?branch=main&style=for-the-badge
|
|
37
|
+
:target: https://github.com/adamchainz/icu4py/actions?workflow=CI
|
|
38
|
+
|
|
39
|
+
.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
|
|
40
|
+
:target: https://github.com/adamchainz/icu4py/actions?workflow=CI
|
|
41
|
+
|
|
42
|
+
.. image:: https://img.shields.io/pypi/v/icu4py.svg?style=for-the-badge
|
|
43
|
+
:target: https://pypi.org/project/icu4py/
|
|
44
|
+
|
|
45
|
+
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
|
|
46
|
+
:target: https://github.com/psf/black
|
|
47
|
+
|
|
48
|
+
.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
|
|
49
|
+
:target: https://github.com/pre-commit/pre-commit
|
|
50
|
+
:alt: pre-commit
|
|
51
|
+
|
|
52
|
+
----
|
|
53
|
+
|
|
54
|
+
Bindings to the ICU (International Components for Unicode) library (ICU4C).
|
|
55
|
+
|
|
56
|
+
Documentation
|
|
57
|
+
=============
|
|
58
|
+
|
|
59
|
+
Please see https://icu4py.readthedocs.io/.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
icu4py-0.1.0.dist-info/RECORD,,
|
|
2
|
+
icu4py-0.1.0.dist-info/WHEEL,sha256=9n06dMtzHWwi4qBAkBXpb3VPUvcCHvHmvzXDwozsM8I,137
|
|
3
|
+
icu4py-0.1.0.dist-info/top_level.txt,sha256=bT8eoOy2eVL3nNv2IXqQo-KpPtbBoR415Kow3HGtId8,7
|
|
4
|
+
icu4py-0.1.0.dist-info/METADATA,sha256=9-bfldKxh0ZrD4T2JWZKqdHL5VTwQw4-g5H8f96jRKs,2286
|
|
5
|
+
icu4py-0.1.0.dist-info/licenses/LICENSE,sha256=blVuFA7Qdp2_CcTyGXLxA4CsHsAjAquxVc4O6ODqye4,1069
|
|
6
|
+
icu4py/messageformat.cpython-314-darwin.so,sha256=zfWG3W2GC9xi9nUIKpMUZrQEIH1O0elwvFpOGU6Cm9c,43760
|
|
7
|
+
icu4py/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
icu4py/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
icu4py/messageformat.pyi,sha256=V7QR4Cc0-Bxmv4wpMqsCODMHAQtDMpukbaupQxzb0XI,188
|
|
10
|
+
icu4py/messageformat.cpp,sha256=XsPfpoB9nmLfvJtz_kU3RA1aSHLdMDfIiPE_AYzbEas,8485
|
|
11
|
+
icu4py/.dylibs/libicui18n.78.1.dylib,sha256=d71FRLcHn398lxGp1IUJyIa-O3zGLPJ1H0hFrZKRUmE,3137456
|
|
12
|
+
icu4py/.dylibs/libicuuc.78.1.dylib,sha256=NayxHCCrsuIufqEtAEhfEdbRldJYCuP5tmlGZvbAc2g,1845024
|
|
13
|
+
icu4py/.dylibs/libicudata.78.1.dylib,sha256=l_QbgSQTFwhdfe9gzshW_5EHuauZtzKRRb02BMUsf-0,33389136
|
|
@@ -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
|
+
icu4py
|