Skip to content

Commit 89d57db

Browse files
committed
Pull upstream chromium-compact-language-detector Part 2
1 parent fef1790 commit 89d57db

5 files changed

Lines changed: 253 additions & 46 deletions

File tree

bindings/README

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@ Dick Sites (and others) at Google graciously provided a new version
66
and I (lucene@mikemccandless.com) created the Python bindings and
77
ported the C++ test case to test.py.
88

9-
This has been tested on Ubuntu 12.10, with both Python 2.7.3 and
10-
3.2.3.
9+
This has been tested on Ubuntu 14.04, with both Python 2.7.6 and
10+
3.4.0.
11+
12+
Updated Nov 11 2014 to the latest CLD2 release, adding new bestEffort
13+
flag (to force a guess even when confidence is low), and cutting over
14+
to the CLD2 methods that confirm incoming UTF-8 is valid.
1115

1216
To build:
1317

14-
* First checkout cld2 and run internal/compile_libs.sh. This will
15-
create both libcld2.so (detects 83 languages) and libcld2_full.so
16-
(detects 163 languages). Install those libraries somewhere on
17-
your LD_LIBRARY_PATH, for example copy them into /usr/lib64.
18+
* First checkout cld2, cd internal, run compile_libs.sh. This will
19+
create both libcld2.so (small tables, detects 83 languages) and
20+
libcld2_full.so (large tables, detects 163 languages). Install
21+
those libraries somewhere on your LD_LIBRARY_PATH, for example
22+
copy them into /usr/lib.
1823

19-
* Edit both setup.py and setup_full.py: edit CLD2_PATH to point to
24+
* Edit both setup.py and setup_full.py: change CLD2_PATH to point to
2025
where you checked out the CLD2 sources.
2126

2227
* python setup.py build

bindings/pycldmodule.cc

Lines changed: 168 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ detect(PyObject *self, PyObject *args, PyObject *kwArgs) {
7171
int flagVerbose = 0;
7272
int flagQuiet = 0;
7373
int flagEcho = 0;
74+
int flagBestEffort = 0;
7475

7576
static const char *kwList[] = {"utf8Bytes",
7677
"isPlainText",
@@ -103,9 +104,15 @@ detect(PyObject *self, PyObject *args, PyObject *kwArgs) {
103104
/* Echo every input buffer to stderr. */
104105
"debugEcho",
105106

107+
/* If true, allow low-quality results for short text,
108+
rather than forcing the result to UNKNOWN_LANGUAGE. This may be of use for
109+
those desiring approximate results on short input text, but there is no claim
110+
that these results ave very good. make a guess at the language even if quality is low (text is short) */
111+
"bestEffort",
112+
106113
NULL};
107114

108-
if (!PyArg_ParseTupleAndKeywords(args, kwArgs, "s#|izzzziiiiiii",
115+
if (!PyArg_ParseTupleAndKeywords(args, kwArgs, "s#|izzzziiiiiiii",
109116
(char **) kwList,
110117
&bytes, &numBytes,
111118
&isPlainText,
@@ -119,7 +126,8 @@ detect(PyObject *self, PyObject *args, PyObject *kwArgs) {
119126
&flagCR,
120127
&flagVerbose,
121128
&flagQuiet,
122-
&flagEcho)) {
129+
&flagEcho,
130+
&flagBestEffort)) {
123131
return 0;
124132
}
125133

@@ -142,6 +150,9 @@ detect(PyObject *self, PyObject *args, PyObject *kwArgs) {
142150
if (flagEcho != 0) {
143151
flags |= CLD2::kCLDFlagEcho;
144152
}
153+
if (flagBestEffort != 0) {
154+
flags |= CLD2::kCLDFlagBestEffort;
155+
}
145156

146157
PyObject *CLDError = GETSTATE(self)->error;
147158

@@ -172,21 +183,28 @@ detect(PyObject *self, PyObject *args, PyObject *kwArgs) {
172183
int percent3[3];
173184
double normalized_score3[3];
174185
int textBytesFound;
186+
int validPrefixBytes;
175187
CLD2::ResultChunkVector resultChunkVector;
176188

177189
Py_BEGIN_ALLOW_THREADS
178-
CLD2::ExtDetectLanguageSummary(bytes, numBytes,
179-
isPlainText != 0,
180-
&cldHints,
181-
flags,
182-
language3,
183-
percent3,
184-
normalized_score3,
185-
returnVectors != 0 ? &resultChunkVector : 0,
186-
&textBytesFound,
187-
&isReliable);
190+
CLD2::ExtDetectLanguageSummaryCheckUTF8(bytes, numBytes,
191+
isPlainText != 0,
192+
&cldHints,
193+
flags,
194+
language3,
195+
percent3,
196+
normalized_score3,
197+
returnVectors != 0 ? &resultChunkVector : 0,
198+
&textBytesFound,
199+
&isReliable,
200+
&validPrefixBytes);
188201
Py_END_ALLOW_THREADS
189202

203+
if (validPrefixBytes < numBytes) {
204+
PyErr_Format(CLDError, "input contains invalid UTF-8 around byte %d (of %d)", validPrefixBytes, numBytes);
205+
return 0;
206+
}
207+
190208
PyObject *details = PyTuple_New(3);
191209
for(int idx=0;idx<3;idx++) {
192210
CLD2::Language lang = language3[idx];
@@ -223,6 +241,7 @@ detect(PyObject *self, PyObject *args, PyObject *kwArgs) {
223241
textBytesFound,
224242
details);
225243
}
244+
226245
Py_DECREF(details);
227246
return result;
228247
}
@@ -231,22 +250,23 @@ const char *DOC =
231250
"Detect language(s) from a UTF8 string.\n\n"
232251

233252
"Arguments:\n\n"
234-
" utf8Bytes: text to detect, encoded as UTF-8 bytes (required)\n\n"
253+
" utf8Bytes: The text to detect, encoded as UTF-8 bytes (required). If\n"
254+
" this is not valid UTF-8, then an cld2.error is raised.\n\n"
235255

236-
" isPlainText: if False, then the input is HTML and CLD will skip HTML tags,\n"
256+
" isPlainText: If False, then the input is HTML and CLD will skip HTML tags,\n"
237257
" expand HTML entities, detect HTML <lang ...> tags, etc.\n\n"
238258

239-
" hintTopLevelDomain: e.g., 'id' boosts Indonesian\n\n"
259+
" hintTopLevelDomain: E.g., 'id' boosts Indonesian\n\n"
240260

241-
" hintLanguage: e.g., 'ITALIAN' or 'it' boosts Italian; see cld.LANGUAGES\n"
261+
" hintLanguage: E.g., 'ITALIAN' or 'it' boosts Italian; see cld.LANGUAGES\n"
242262
" for all known language\n\n"
243263

244-
" hintLanguageHTTPHeaders: e.g., 'mi,en' boosts Maori and English\n\n"
264+
" hintLanguageHTTPHeaders: E.g., 'mi,en' boosts Maori and English\n\n"
245265

246-
" hintEncoding: e.g, 'SJS' boosts Japanese; see cld.ENCODINGS for all known\n"
266+
" hintEncoding: E.g, 'SJS' boosts Japanese; see cld.ENCODINGS for all known\n"
247267
" encodings\n\n"
248268

249-
" returnVectors: if True then the vectors indicating which language was\n"
269+
" returnVectors: If True then the vectors indicating which language was\n"
250270
" detected in which byte range are returned in addition to\n"
251271
" details. The vectors are a sequence of (bytesOffset,\n"
252272
" bytesLength, languageName, languageCode), in order.\n"
@@ -265,6 +285,12 @@ const char *DOC =
265285
" text chunks and their detected languages. See\n"
266286
" docs/InterpretingCLD2UnitTestOutput.pdf to interpret this output.\n\n"
267287

288+
" bestEffort: If True then allow low-quality results for short text,\n"
289+
" rather than forcing the result to UNKNOWN_LANGUAGE. This\n"
290+
" may be of use for those desiring approximate results on\n"
291+
" short input text, but there is no claim that these result\n"
292+
" are very good.\n\n"
293+
268294
" debugCR: In that HTML file, force a new line for each chunk.\n\n"
269295

270296
" debugVerbose: In that HTML file, show every lookup entry.\n\n"
@@ -320,21 +346,33 @@ static struct PyModuleDef moduledef = {
320346

321347
//PyObject *
322348
PyMODINIT_FUNC
323-
PyInit__pycld2(void)
349+
#ifdef CLD2_FULL
350+
PyInit_cld2full(void)
351+
#else
352+
PyInit_cld2(void)
353+
#endif
324354

325355
#else // IS_PY3K
326356

327357
#define INITERROR return
328358

329359
PyMODINIT_FUNC
330-
init_pycld2()
360+
#ifdef CLD2_FULL
361+
initcld2full()
362+
#else
363+
initcld2()
364+
#endif
331365
#endif
332366
{
333367

334368
#ifdef IS_PY3K
335369
PyObject *m = PyModule_Create(&moduledef);
336370
#else
337-
PyObject* m = Py_InitModule("_pycld2", CLDMethods);
371+
#ifdef CLD2_FULL
372+
PyObject* m = Py_InitModule("cld2full", CLDMethods);
373+
#else
374+
PyObject* m = Py_InitModule("cld2", CLDMethods);
375+
#endif
338376
#endif
339377

340378
if (m == NULL) {
@@ -343,7 +381,12 @@ init_pycld2()
343381

344382
struct PYCLDState *st = GETSTATE(m);
345383

346-
st->error = PyErr_NewException((char *) "cld.error", NULL, NULL);
384+
#ifdef CLD2_FULL
385+
st->error = PyErr_NewException((char *) "cld2full.error", NULL, NULL);
386+
#else
387+
st->error = PyErr_NewException((char *) "cld2.error", NULL, NULL);
388+
#endif
389+
347390
if (st->error == NULL) {
348391
Py_DECREF(m);
349392
INITERROR;
@@ -410,6 +453,7 @@ init_pycld2()
410453

411454
upto = 0;
412455

456+
#ifdef CLD2_FULL
413457
PyObject* detLangs = PyTuple_New(165);
414458

415459
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ABKHAZIAN"));
@@ -577,7 +621,108 @@ init_pycld2()
577621
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("YORUBA"));
578622
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ZHUANG"));
579623
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ZULU"));
624+
#else
625+
PyObject* detLangs = PyTuple_New(94);
580626

627+
// List originally sent by Dick Sites on 7/17/2013, then I
628+
// added 6 new languages from the Jan 2014 release, and
629+
// removed 5 and added 13 langs from the Oct 2014 release:
630+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("AFRIKAANS"));
631+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ALBANIAN"));
632+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ARABIC"));
633+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ARMENIAN"));
634+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("AZERBAIJANI"));
635+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BASQUE"));
636+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BELARUSIAN"));
637+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BENGALI"));
638+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BIHARI"));
639+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BOSNIAN"));
640+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BULGARIAN"));
641+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("BURMESE"));
642+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("CATALAN"));
643+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("CEBUANO"));
644+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("CHEROKEE"));
645+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("CROATIAN"));
646+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("CZECH"));
647+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("Chinese"));
648+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ChineseT"));
649+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("DANISH"));
650+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("DHIVEHI"));
651+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("DUTCH"));
652+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ENGLISH"));
653+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ESTONIAN"));
654+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("FINNISH"));
655+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("FRENCH"));
656+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("GALICIAN"));
657+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("GANDA"));
658+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("GEORGIAN"));
659+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("GERMAN"));
660+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("GREEK"));
661+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("GUJARATI"));
662+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("HAITIAN_CREOLE"));
663+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("HEBREW"));
664+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("HINDI"));
665+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("HMONG"));
666+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("HUNGARIAN"));
667+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ICELANDIC"));
668+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("INDONESIAN"));
669+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("INUKTITUT"));
670+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("IRISH"));
671+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ITALIAN"));
672+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("JAVANESE"));
673+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("Japanese"));
674+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("KANNADA"));
675+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("KAZAKH"));
676+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("KHMER"));
677+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("KINYARWANDA"));
678+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("KURDISH"));
679+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("KYRGYZ"));
680+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("Korean"));
681+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("LAOTHIAN"));
682+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("LATVIAN"));
683+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("LIMBU"));
684+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("LITHUANIAN"));
685+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("MACEDONIAN"));
686+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("MALAGASY"));
687+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("MALAY"));
688+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("MALAYALAM"));
689+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("MALTESE"));
690+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("MARATHI"));
691+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("NEPALI"));
692+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("NORWEGIAN"));
693+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("NYANJA"));
694+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ORIYA"));
695+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("PERSIAN"));
696+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("POLISH"));
697+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("PORTUGUESE"));
698+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("PUNJABI"));
699+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("ROMANIAN"));
700+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("RUSSIAN"));
701+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SCOTS_GAELIC"));
702+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SERBIAN"));
703+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SESOTHO"));
704+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SINHALESE"));
705+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SLOVAK"));
706+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SLOVENIAN"));
707+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SPANISH"));
708+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SUNDANESE"));
709+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SWAHILI"));
710+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SWEDISH"));
711+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("SYRIAC"));
712+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("TAGALOG"));
713+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("TAJIK"));
714+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("TAMIL"));
715+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("TELUGU"));
716+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("THAI"));
717+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("TURKISH"));
718+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("UKRAINIAN"));
719+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("URDU"));
720+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("UZBEK"));
721+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("VIETNAMESE"));
722+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("WELSH"));
723+
PyTuple_SET_ITEM(detLangs, upto++, PyUnicode_FromString("YIDDISH"));
724+
#endif
725+
581726
// Steals ref:
582727
PyModule_AddObject(m, "DETECTED_LANGUAGES", detLangs);
583728

bindings/setup.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
import subprocess
2121
import sys
2222
import os
23+
import shutil
2324

25+
if os.path.exists('build'):
26+
shutil.rmtree('build')
27+
2428
# NOTE: change this to point to where you checked out the CLD2
2529
# sources:
2630
CLD2_PATH = '../cld2'

bindings/setup_full.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
import subprocess
2121
import sys
2222
import os
23+
import shutil
24+
25+
if os.path.exists('build'):
26+
shutil.rmtree('build')
2327

2428
# NOTE: change this to point to where you checked out the CLD2
2529
# sources:

0 commit comments

Comments
 (0)