nltk 3.2__zip → 3.3__zip

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 (406) hide show
  1. {nltk-3.2 → nltk-3.3}/INSTALL.txt +6 -6
  2. {nltk-3.2 → nltk-3.3}/LICENSE.txt +13 -13
  3. {nltk-3.2 → nltk-3.3}/MANIFEST.in +6 -6
  4. {nltk-3.2/nltk.egg-info → nltk-3.3}/PKG-INFO +13 -5
  5. nltk-3.2/README.txt → nltk-3.3/README.md +49 -48
  6. nltk-3.3/nltk/VERSION +1 -0
  7. {nltk-3.2 → nltk-3.3}/nltk/__init__.py +185 -185
  8. {nltk-3.2 → nltk-3.3}/nltk/app/__init__.py +52 -53
  9. {nltk-3.2 → nltk-3.3}/nltk/app/chartparser_app.py +2276 -2276
  10. {nltk-3.2 → nltk-3.3}/nltk/app/chunkparser_app.py +1262 -1264
  11. {nltk-3.2 → nltk-3.3}/nltk/app/collocations_app.py +347 -350
  12. {nltk-3.2 → nltk-3.3}/nltk/app/concordance_app.py +568 -570
  13. {nltk-3.2 → nltk-3.3}/nltk/app/nemo_app.py +156 -155
  14. {nltk-3.2 → nltk-3.3}/nltk/app/rdparser_app.py +892 -894
  15. {nltk-3.2 → nltk-3.3}/nltk/app/srparser_app.py +809 -809
  16. {nltk-3.2 → nltk-3.3}/nltk/app/wordfreq_app.py +32 -32
  17. {nltk-3.2 → nltk-3.3}/nltk/app/wordnet_app.py +968 -968
  18. {nltk-3.2 → nltk-3.3}/nltk/book.py +91 -90
  19. {nltk-3.2 → nltk-3.3}/nltk/ccg/__init__.py +22 -22
  20. {nltk-3.2 → nltk-3.3}/nltk/ccg/api.py +359 -335
  21. {nltk-3.2 → nltk-3.3}/nltk/ccg/chart.py +394 -361
  22. {nltk-3.2 → nltk-3.3}/nltk/ccg/combinator.py +343 -312
  23. {nltk-3.2 → nltk-3.3}/nltk/ccg/lexicon.py +328 -290
  24. nltk-3.3/nltk/ccg/logic.py +46 -0
  25. {nltk-3.2 → nltk-3.3}/nltk/chat/__init__.py +49 -49
  26. {nltk-3.2 → nltk-3.3}/nltk/chat/eliza.py +244 -244
  27. {nltk-3.2 → nltk-3.3}/nltk/chat/iesha.py +140 -140
  28. {nltk-3.2 → nltk-3.3}/nltk/chat/rude.py +92 -92
  29. {nltk-3.2 → nltk-3.3}/nltk/chat/suntsu.py +117 -117
  30. {nltk-3.2 → nltk-3.3}/nltk/chat/util.py +122 -120
  31. {nltk-3.2 → nltk-3.3}/nltk/chat/zen.py +282 -282
  32. {nltk-3.2 → nltk-3.3}/nltk/chunk/__init__.py +190 -190
  33. {nltk-3.2 → nltk-3.3}/nltk/chunk/api.py +51 -51
  34. {nltk-3.2 → nltk-3.3}/nltk/chunk/named_entity.py +331 -331
  35. {nltk-3.2 → nltk-3.3}/nltk/chunk/regexp.py +1390 -1384
  36. {nltk-3.2 → nltk-3.3}/nltk/chunk/util.py +600 -600
  37. {nltk-3.2 → nltk-3.3}/nltk/classify/__init__.py +98 -98
  38. {nltk-3.2 → nltk-3.3}/nltk/classify/api.py +193 -193
  39. {nltk-3.2 → nltk-3.3}/nltk/classify/decisiontree.py +295 -295
  40. {nltk-3.2 → nltk-3.3}/nltk/classify/maxent.py +1510 -1492
  41. {nltk-3.2 → nltk-3.3}/nltk/classify/megam.py +179 -178
  42. {nltk-3.2 → nltk-3.3}/nltk/classify/naivebayes.py +245 -242
  43. {nltk-3.2 → nltk-3.3}/nltk/classify/positivenaivebayes.py +170 -170
  44. {nltk-3.2 → nltk-3.3}/nltk/classify/rte_classify.py +160 -191
  45. {nltk-3.2 → nltk-3.3}/nltk/classify/scikitlearn.py +153 -151
  46. {nltk-3.2 → nltk-3.3}/nltk/classify/senna.py +183 -181
  47. {nltk-3.2 → nltk-3.3}/nltk/classify/svm.py +15 -15
  48. {nltk-3.2 → nltk-3.3}/nltk/classify/tadm.py +113 -112
  49. {nltk-3.2 → nltk-3.3}/nltk/classify/textcat.py +193 -193
  50. {nltk-3.2 → nltk-3.3}/nltk/classify/util.py +324 -312
  51. {nltk-3.2 → nltk-3.3}/nltk/classify/weka.py +346 -345
  52. {nltk-3.2 → nltk-3.3}/nltk/cluster/__init__.py +86 -86
  53. {nltk-3.2 → nltk-3.3}/nltk/cluster/api.py +74 -70
  54. {nltk-3.2 → nltk-3.3}/nltk/cluster/em.py +247 -247
  55. {nltk-3.2 → nltk-3.3}/nltk/cluster/gaac.py +168 -168
  56. {nltk-3.2 → nltk-3.3}/nltk/cluster/kmeans.py +221 -221
  57. {nltk-3.2 → nltk-3.3}/nltk/cluster/util.py +304 -289
  58. nltk-3.3/nltk/collections.py +640 -0
  59. {nltk-3.2 → nltk-3.3}/nltk/collocations.py +388 -388
  60. nltk-3.3/nltk/compat.py +365 -0
  61. {nltk-3.2 → nltk-3.3}/nltk/corpus/__init__.py +341 -320
  62. {nltk-3.2 → nltk-3.3}/nltk/corpus/europarl_raw.py +44 -44
  63. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/__init__.py +147 -145
  64. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/aligned.py +115 -114
  65. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/api.py +448 -446
  66. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bnc.py +252 -252
  67. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bracket_parse.py +227 -227
  68. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/categorized_sents.py +179 -178
  69. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chasen.py +140 -139
  70. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/childes.py +522 -494
  71. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chunked.py +212 -212
  72. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/cmudict.py +97 -95
  73. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/comparative_sents.py +280 -278
  74. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/conll.py +528 -521
  75. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/crubadan.py +116 -116
  76. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/dependency.py +101 -101
  77. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/framenet.py +3059 -2093
  78. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ieer.py +112 -111
  79. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/indian.py +85 -86
  80. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ipipan.py +332 -331
  81. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/knbc.py +187 -187
  82. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/lin.py +156 -156
  83. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/mte.py +311 -310
  84. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nkjp.py +429 -428
  85. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nombank.py +421 -421
  86. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nps_chat.py +73 -73
  87. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/opinion_lexicon.py +115 -115
  88. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/panlex_lite.py +165 -165
  89. nltk-3.3/nltk/corpus/reader/pl196x.py +292 -0
  90. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/plaintext.py +232 -233
  91. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ppattach.py +96 -95
  92. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/propbank.py +479 -481
  93. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/pros_cons.py +128 -126
  94. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/reviews.py +330 -328
  95. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/rte.py +144 -146
  96. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/semcor.py +256 -256
  97. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/senseval.py +203 -201
  98. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sentiwordnet.py +137 -137
  99. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sinica_treebank.py +75 -75
  100. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/string_category.py +60 -59
  101. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/switchboard.py +119 -119
  102. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/tagged.py +295 -294
  103. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/timit.py +452 -450
  104. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/toolbox.py +68 -68
  105. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/twitter.py +155 -157
  106. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/udhr.py +80 -80
  107. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/util.py +804 -800
  108. nltk-3.3/nltk/corpus/reader/verbnet.py +595 -0
  109. nltk-3.3/nltk/corpus/reader/wordlist.py +134 -0
  110. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/wordnet.py +2108 -2036
  111. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/xmldocs.py +392 -387
  112. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ycoe.py +243 -242
  113. {nltk-3.2 → nltk-3.3}/nltk/corpus/util.py +144 -127
  114. {nltk-3.2 → nltk-3.3}/nltk/data.py +1487 -1451
  115. {nltk-3.2 → nltk-3.3}/nltk/decorators.py +219 -215
  116. {nltk-3.2 → nltk-3.3}/nltk/downloader.py +2278 -2274
  117. {nltk-3.2 → nltk-3.3}/nltk/draw/__init__.py +27 -27
  118. {nltk-3.2 → nltk-3.3}/nltk/draw/cfg.py +774 -777
  119. {nltk-3.2 → nltk-3.3}/nltk/draw/dispersion.py +58 -58
  120. {nltk-3.2 → nltk-3.3}/nltk/draw/table.py +1098 -1098
  121. {nltk-3.2 → nltk-3.3}/nltk/draw/tree.py +963 -966
  122. {nltk-3.2 → nltk-3.3}/nltk/draw/util.py +2356 -2356
  123. {nltk-3.2 → nltk-3.3}/nltk/featstruct.py +2617 -2500
  124. {nltk-3.2 → nltk-3.3}/nltk/grammar.py +1559 -1516
  125. {nltk-3.2 → nltk-3.3}/nltk/help.py +56 -56
  126. {nltk-3.2 → nltk-3.3}/nltk/inference/__init__.py +20 -20
  127. {nltk-3.2 → nltk-3.3}/nltk/inference/api.py +597 -589
  128. {nltk-3.2 → nltk-3.3}/nltk/inference/discourse.py +612 -608
  129. {nltk-3.2 → nltk-3.3}/nltk/inference/mace.py +311 -311
  130. {nltk-3.2 → nltk-3.3}/nltk/inference/nonmonotonic.py +509 -509
  131. {nltk-3.2 → nltk-3.3}/nltk/inference/prover9.py +431 -431
  132. {nltk-3.2 → nltk-3.3}/nltk/inference/resolution.py +687 -687
  133. {nltk-3.2 → nltk-3.3}/nltk/inference/tableau.py +607 -607
  134. {nltk-3.2 → nltk-3.3}/nltk/internals.py +982 -982
  135. {nltk-3.2 → nltk-3.3}/nltk/jsontags.py +63 -63
  136. {nltk-3.2 → nltk-3.3}/nltk/lazyimport.py +142 -142
  137. {nltk-3.2 → nltk-3.3}/nltk/metrics/__init__.py +30 -29
  138. {nltk-3.2 → nltk-3.3}/nltk/metrics/agreement.py +421 -401
  139. nltk-3.3/nltk/metrics/aline.py +607 -0
  140. {nltk-3.2 → nltk-3.3}/nltk/metrics/association.py +414 -410
  141. {nltk-3.2 → nltk-3.3}/nltk/metrics/confusionmatrix.py +206 -206
  142. {nltk-3.2 → nltk-3.3}/nltk/metrics/distance.py +207 -202
  143. {nltk-3.2 → nltk-3.3}/nltk/metrics/paice.py +381 -381
  144. {nltk-3.2 → nltk-3.3}/nltk/metrics/scores.py +228 -228
  145. {nltk-3.2 → nltk-3.3}/nltk/metrics/segmentation.py +231 -232
  146. {nltk-3.2 → nltk-3.3}/nltk/metrics/spearman.py +69 -69
  147. {nltk-3.2 → nltk-3.3}/nltk/misc/__init__.py +11 -11
  148. {nltk-3.2 → nltk-3.3}/nltk/misc/babelfish.py +10 -10
  149. {nltk-3.2 → nltk-3.3}/nltk/misc/chomsky.py +135 -133
  150. {nltk-3.2 → nltk-3.3}/nltk/misc/minimalset.py +83 -83
  151. {nltk-3.2 → nltk-3.3}/nltk/misc/sort.py +157 -157
  152. {nltk-3.2 → nltk-3.3}/nltk/misc/wordfinder.py +129 -129
  153. {nltk-3.2 → nltk-3.3}/nltk/parse/__init__.py +81 -80
  154. {nltk-3.2 → nltk-3.3}/nltk/parse/api.py +66 -66
  155. {nltk-3.2 → nltk-3.3}/nltk/parse/bllip.py +282 -282
  156. {nltk-3.2 → nltk-3.3}/nltk/parse/chart.py +1682 -1681
  157. nltk-3.3/nltk/parse/corenlp.py +780 -0
  158. {nltk-3.2 → nltk-3.3}/nltk/parse/dependencygraph.py +760 -758
  159. {nltk-3.2 → nltk-3.3}/nltk/parse/earleychart.py +452 -451
  160. {nltk-3.2 → nltk-3.3}/nltk/parse/evaluate.py +129 -129
  161. {nltk-3.2 → nltk-3.3}/nltk/parse/featurechart.py +580 -578
  162. {nltk-3.2 → nltk-3.3}/nltk/parse/generate.py +87 -76
  163. {nltk-3.2 → nltk-3.3}/nltk/parse/malt.py +356 -356
  164. {nltk-3.2 → nltk-3.3}/nltk/parse/nonprojectivedependencyparser.py +783 -783
  165. {nltk-3.2 → nltk-3.3}/nltk/parse/pchart.py +522 -522
  166. {nltk-3.2 → nltk-3.3}/nltk/parse/projectivedependencyparser.py +579 -578
  167. {nltk-3.2 → nltk-3.3}/nltk/parse/recursivedescent.py +655 -656
  168. {nltk-3.2 → nltk-3.3}/nltk/parse/shiftreduce.py +458 -459
  169. {nltk-3.2 → nltk-3.3}/nltk/parse/stanford.py +431 -406
  170. {nltk-3.2 → nltk-3.3}/nltk/parse/transitionparser.py +774 -772
  171. {nltk-3.2 → nltk-3.3}/nltk/parse/util.py +231 -231
  172. {nltk-3.2 → nltk-3.3}/nltk/parse/viterbi.py +401 -401
  173. {nltk-3.2 → nltk-3.3}/nltk/probability.py +2410 -2355
  174. {nltk-3.2 → nltk-3.3}/nltk/sem/__init__.py +61 -61
  175. {nltk-3.2 → nltk-3.3}/nltk/sem/boxer.py +1261 -1261
  176. {nltk-3.2 → nltk-3.3}/nltk/sem/chat80.py +782 -782
  177. {nltk-3.2 → nltk-3.3}/nltk/sem/cooper_storage.py +118 -118
  178. {nltk-3.2 → nltk-3.3}/nltk/sem/drt.py +1258 -1257
  179. {nltk-3.2 → nltk-3.3}/nltk/sem/drt_glue_demo.py +485 -489
  180. {nltk-3.2 → nltk-3.3}/nltk/sem/evaluate.py +790 -784
  181. {nltk-3.2 → nltk-3.3}/nltk/sem/glue.py +696 -694
  182. {nltk-3.2 → nltk-3.3}/nltk/sem/hole.py +386 -384
  183. {nltk-3.2 → nltk-3.3}/nltk/sem/lfg.py +210 -208
  184. {nltk-3.2 → nltk-3.3}/nltk/sem/linearlogic.py +451 -449
  185. {nltk-3.2 → nltk-3.3}/nltk/sem/logic.py +1910 -1910
  186. {nltk-3.2 → nltk-3.3}/nltk/sem/relextract.py +476 -483
  187. {nltk-3.2 → nltk-3.3}/nltk/sem/skolemize.py +101 -101
  188. {nltk-3.2 → nltk-3.3}/nltk/sem/util.py +249 -249
  189. {nltk-3.2 → nltk-3.3}/nltk/sentiment/__init__.py +14 -14
  190. {nltk-3.2 → nltk-3.3}/nltk/sentiment/sentiment_analyzer.py +229 -229
  191. {nltk-3.2 → nltk-3.3}/nltk/sentiment/util.py +762 -752
  192. {nltk-3.2 → nltk-3.3}/nltk/sentiment/vader.py +443 -445
  193. {nltk-3.2 → nltk-3.3}/nltk/stem/__init__.py +31 -33
  194. {nltk-3.2 → nltk-3.3}/nltk/stem/api.py +28 -25
  195. nltk-3.3/nltk/stem/arlstem.py +369 -0
  196. {nltk-3.2 → nltk-3.3}/nltk/stem/isri.py +345 -345
  197. {nltk-3.2 → nltk-3.3}/nltk/stem/lancaster.py +337 -310
  198. nltk-3.3/nltk/stem/porter.py +710 -0
  199. {nltk-3.2 → nltk-3.3}/nltk/stem/regexp.py +61 -61
  200. {nltk-3.2 → nltk-3.3}/nltk/stem/rslp.py +142 -142
  201. {nltk-3.2 → nltk-3.3}/nltk/stem/snowball.py +4236 -3712
  202. {nltk-3.2 → nltk-3.3}/nltk/stem/util.py +22 -12
  203. {nltk-3.2 → nltk-3.3}/nltk/stem/wordnet.py +51 -51
  204. {nltk-3.2 → nltk-3.3}/nltk/tag/__init__.py +152 -127
  205. {nltk-3.2 → nltk-3.3}/nltk/tag/api.py +86 -81
  206. {nltk-3.2 → nltk-3.3}/nltk/tag/brill.py +424 -426
  207. {nltk-3.2 → nltk-3.3}/nltk/tag/brill_trainer.py +608 -608
  208. {nltk-3.2 → nltk-3.3}/nltk/tag/crf.py +204 -200
  209. {nltk-3.2 → nltk-3.3}/nltk/tag/hmm.py +1277 -1277
  210. {nltk-3.2 → nltk-3.3}/nltk/tag/hunpos.py +132 -132
  211. {nltk-3.2 → nltk-3.3}/nltk/tag/mapping.py +101 -101
  212. {nltk-3.2 → nltk-3.3}/nltk/tag/perceptron.py +329 -314
  213. {nltk-3.2 → nltk-3.3}/nltk/tag/senna.py +147 -145
  214. {nltk-3.2 → nltk-3.3}/nltk/tag/sequential.py +740 -736
  215. {nltk-3.2 → nltk-3.3}/nltk/tag/stanford.py +219 -196
  216. {nltk-3.2 → nltk-3.3}/nltk/tag/tnt.py +595 -604
  217. {nltk-3.2 → nltk-3.3}/nltk/tag/util.py +72 -72
  218. {nltk-3.2 → nltk-3.3}/nltk/tbl/__init__.py +28 -28
  219. {nltk-3.2 → nltk-3.3}/nltk/tbl/api.py +1 -1
  220. {nltk-3.2 → nltk-3.3}/nltk/tbl/demo.py +366 -366
  221. {nltk-3.2 → nltk-3.3}/nltk/tbl/erroranalysis.py +39 -39
  222. {nltk-3.2 → nltk-3.3}/nltk/tbl/feature.py +259 -261
  223. {nltk-3.2 → nltk-3.3}/nltk/tbl/rule.py +321 -321
  224. {nltk-3.2 → nltk-3.3}/nltk/tbl/template.py +303 -308
  225. {nltk-3.2 → nltk-3.3}/nltk/test/__init__.py +18 -18
  226. {nltk-3.2 → nltk-3.3}/nltk/test/all.py +24 -23
  227. {nltk-3.2 → nltk-3.3}/nltk/test/bleu.doctest +14 -14
  228. {nltk-3.2 → nltk-3.3}/nltk/test/bnc.doctest +60 -60
  229. {nltk-3.2 → nltk-3.3}/nltk/test/ccg.doctest +376 -395
  230. nltk-3.3/nltk/test/ccg_semantics.doctest +553 -0
  231. {nltk-3.2 → nltk-3.3}/nltk/test/chat80.doctest +234 -234
  232. {nltk-3.2 → nltk-3.3}/nltk/test/childes.doctest +184 -184
  233. {nltk-3.2 → nltk-3.3}/nltk/test/childes_fixt.py +14 -13
  234. {nltk-3.2 → nltk-3.3}/nltk/test/chunk.doctest +373 -373
  235. {nltk-3.2 → nltk-3.3}/nltk/test/classify.doctest +185 -185
  236. {nltk-3.2 → nltk-3.3}/nltk/test/classify_fixt.py +11 -10
  237. nltk-3.3/nltk/test/collections.doctest +20 -0
  238. {nltk-3.2 → nltk-3.3}/nltk/test/collocations.doctest +276 -276
  239. {nltk-3.2 → nltk-3.3}/nltk/test/compat.doctest +134 -138
  240. {nltk-3.2 → nltk-3.3}/nltk/test/compat_fixt.py +9 -8
  241. nltk-3.3/nltk/test/concordance.doctest +55 -0
  242. {nltk-3.2 → nltk-3.3}/nltk/test/corpus.doctest +2200 -2212
  243. {nltk-3.2 → nltk-3.3}/nltk/test/corpus_fixt.py +3 -3
  244. {nltk-3.2 → nltk-3.3}/nltk/test/crubadan.doctest +65 -65
  245. {nltk-3.2 → nltk-3.3}/nltk/test/data.doctest +379 -379
  246. {nltk-3.2 → nltk-3.3}/nltk/test/dependency.doctest +241 -241
  247. {nltk-3.2 → nltk-3.3}/nltk/test/discourse.doctest +546 -546
  248. {nltk-3.2 → nltk-3.3}/nltk/test/discourse_fixt.py +14 -13
  249. {nltk-3.2 → nltk-3.3}/nltk/test/doctest_nose_plugin.py +159 -156
  250. {nltk-3.2 → nltk-3.3}/nltk/test/drt.doctest +517 -517
  251. {nltk-3.2 → nltk-3.3}/nltk/test/featgram.doctest +607 -607
  252. {nltk-3.2 → nltk-3.3}/nltk/test/featstruct.doctest +1229 -1230
  253. {nltk-3.2 → nltk-3.3}/nltk/test/framenet.doctest +288 -239
  254. {nltk-3.2 → nltk-3.3}/nltk/test/generate.doctest +67 -67
  255. {nltk-3.2 → nltk-3.3}/nltk/test/gensim.doctest +140 -141
  256. {nltk-3.2 → nltk-3.3}/nltk/test/gensim_fixt.py +10 -9
  257. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics.doctest +384 -386
  258. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt.doctest +68 -68
  259. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt_fixt.py +12 -11
  260. {nltk-3.2 → nltk-3.3}/nltk/test/grammar.doctest +48 -48
  261. {nltk-3.2 → nltk-3.3}/nltk/test/grammartestsuites.doctest +109 -109
  262. {nltk-3.2 → nltk-3.3}/nltk/test/index.doctest +100 -100
  263. {nltk-3.2 → nltk-3.3}/nltk/test/inference.doctest +534 -534
  264. {nltk-3.2 → nltk-3.3}/nltk/test/inference_fixt.py +12 -11
  265. {nltk-3.2 → nltk-3.3}/nltk/test/internals.doctest +140 -140
  266. {nltk-3.2 → nltk-3.3}/nltk/test/japanese.doctest +48 -48
  267. {nltk-3.2 → nltk-3.3}/nltk/test/logic.doctest +1098 -1098
  268. {nltk-3.2 → nltk-3.3}/nltk/test/metrics.doctest +270 -270
  269. {nltk-3.2 → nltk-3.3}/nltk/test/misc.doctest +118 -118
  270. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic.doctest +286 -286
  271. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic_fixt.py +12 -11
  272. {nltk-3.2 → nltk-3.3}/nltk/test/paice.doctest +35 -35
  273. {nltk-3.2 → nltk-3.3}/nltk/test/parse.doctest +884 -884
  274. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en.doctest +565 -565
  275. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en_fixt.py +16 -16
  276. {nltk-3.2 → nltk-3.3}/nltk/test/probability.doctest +304 -304
  277. {nltk-3.2 → nltk-3.3}/nltk/test/probability_fixt.py +13 -12
  278. {nltk-3.2 → nltk-3.3}/nltk/test/propbank.doctest +176 -176
  279. {nltk-3.2 → nltk-3.3}/nltk/test/relextract.doctest +263 -263
  280. {nltk-3.2 → nltk-3.3}/nltk/test/resolution.doctest +221 -221
  281. {nltk-3.2 → nltk-3.3}/nltk/test/runtests.py +86 -64
  282. {nltk-3.2 → nltk-3.3}/nltk/test/segmentation_fixt.py +10 -10
  283. {nltk-3.2 → nltk-3.3}/nltk/test/semantics.doctest +665 -665
  284. {nltk-3.2 → nltk-3.3}/nltk/test/semantics_fixt.py +7 -7
  285. {nltk-3.2 → nltk-3.3}/nltk/test/sentiment.doctest +233 -233
  286. {nltk-3.2 → nltk-3.3}/nltk/test/sentiwordnet.doctest +41 -41
  287. {nltk-3.2 → nltk-3.3}/nltk/test/simple.doctest +85 -85
  288. {nltk-3.2 → nltk-3.3}/nltk/test/stem.doctest +78 -78
  289. {nltk-3.2 → nltk-3.3}/nltk/test/tag.doctest +33 -33
  290. {nltk-3.2 → nltk-3.3}/nltk/test/tokenize.doctest +273 -166
  291. {nltk-3.2 → nltk-3.3}/nltk/test/toolbox.doctest +307 -307
  292. {nltk-3.2 → nltk-3.3}/nltk/test/translate.doctest +242 -242
  293. {nltk-3.2 → nltk-3.3}/nltk/test/translate_fixt.py +3 -3
  294. {nltk-3.2 → nltk-3.3}/nltk/test/tree.doctest +1101 -1101
  295. {nltk-3.2 → nltk-3.3}/nltk/test/treeprettyprinter.doctest +127 -127
  296. {nltk-3.2 → nltk-3.3}/nltk/test/treetransforms.doctest +156 -156
  297. nltk-3.3/nltk/test/unit/test_2x_compat.py +63 -0
  298. nltk-3.3/nltk/test/unit/test_aline.py +52 -0
  299. nltk-3.3/nltk/test/unit/test_chunk.py +56 -0
  300. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_classify.py +55 -53
  301. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_collocations.py +88 -79
  302. nltk-3.3/nltk/test/unit/test_concordance.py +98 -0
  303. nltk-3.3/nltk/test/unit/test_corenlp.py +1000 -0
  304. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpora.py +200 -183
  305. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpus_views.py +45 -45
  306. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_hmm.py +88 -88
  307. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_json2csv_corpus.py +188 -187
  308. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_naivebayes.py +26 -26
  309. nltk-3.3/nltk/test/unit/test_rte_classify.py +86 -0
  310. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_seekable_unicode_stream_reader.py +124 -124
  311. nltk-3.3/nltk/test/unit/test_senna.py +74 -0
  312. nltk-3.3/nltk/test/unit/test_stem.py +121 -0
  313. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tag.py +20 -20
  314. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tgrep.py +628 -626
  315. nltk-3.3/nltk/test/unit/test_tokenize.py +168 -0
  316. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_twitter_auth.py +185 -185
  317. nltk-3.3/nltk/test/unit/test_wordnet.py +134 -0
  318. nltk-3.3/nltk/test/unit/translate/test_bleu.py +244 -0
  319. nltk-3.3/nltk/test/unit/translate/test_gdfa.py +57 -0
  320. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm1.py +75 -75
  321. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm2.py +87 -87
  322. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm3.py +105 -105
  323. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm4.py +126 -126
  324. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm5.py +166 -166
  325. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm_model.py +270 -270
  326. nltk-3.3/nltk/test/unit/translate/test_nist.py +37 -0
  327. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_stack_decoder.py +300 -300
  328. {nltk-3.2 → nltk-3.3}/nltk/test/unit/utils.py +41 -41
  329. {nltk-3.2 → nltk-3.3}/nltk/test/util.doctest +48 -48
  330. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet.doctest +595 -581
  331. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_fixt.py +7 -6
  332. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_lch.doctest +53 -53
  333. {nltk-3.2 → nltk-3.3}/nltk/test/wsd.doctest +68 -68
  334. {nltk-3.2 → nltk-3.3}/nltk/text.py +663 -610
  335. {nltk-3.2 → nltk-3.3}/nltk/tgrep.py +938 -937
  336. {nltk-3.2 → nltk-3.3}/nltk/tokenize/__init__.py +130 -107
  337. {nltk-3.2 → nltk-3.3}/nltk/tokenize/api.py +77 -78
  338. {nltk-3.2 → nltk-3.3}/nltk/tokenize/casual.py +343 -342
  339. {nltk-3.2 → nltk-3.3}/nltk/tokenize/mwe.py +120 -120
  340. nltk-3.3/nltk/tokenize/nist.py +173 -0
  341. {nltk-3.2 → nltk-3.3}/nltk/tokenize/punkt.py +1620 -1601
  342. {nltk-3.2 → nltk-3.3}/nltk/tokenize/regexp.py +208 -202
  343. nltk-3.3/nltk/tokenize/repp.py +151 -0
  344. {nltk-3.2 → nltk-3.3}/nltk/tokenize/sexpr.py +140 -142
  345. {nltk-3.2 → nltk-3.3}/nltk/tokenize/simple.py +138 -136
  346. {nltk-3.2 → nltk-3.3}/nltk/tokenize/stanford.py +117 -113
  347. nltk-3.3/nltk/tokenize/stanford_segmenter.py +246 -0
  348. {nltk-3.2 → nltk-3.3}/nltk/tokenize/texttiling.py +458 -460
  349. nltk-3.3/nltk/tokenize/toktok.py +155 -0
  350. nltk-3.3/nltk/tokenize/treebank.py +363 -0
  351. nltk-3.3/nltk/tokenize/util.py +261 -0
  352. {nltk-3.2 → nltk-3.3}/nltk/toolbox.py +499 -497
  353. {nltk-3.2 → nltk-3.3}/nltk/translate/__init__.py +24 -24
  354. {nltk-3.2 → nltk-3.3}/nltk/translate/api.py +331 -321
  355. {nltk-3.2 → nltk-3.3}/nltk/translate/bleu_score.py +615 -556
  356. nltk-3.3/nltk/translate/chrf_score.py +137 -0
  357. {nltk-3.2 → nltk-3.3}/nltk/translate/gale_church.py +248 -235
  358. {nltk-3.2 → nltk-3.3}/nltk/translate/gdfa.py +135 -131
  359. nltk-3.3/nltk/translate/gleu_score.py +193 -0
  360. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm1.py +250 -241
  361. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm2.py +308 -308
  362. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm3.py +337 -337
  363. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm4.py +467 -467
  364. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm5.py +639 -639
  365. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm_model.py +536 -533
  366. {nltk-3.2 → nltk-3.3}/nltk/translate/metrics.py +40 -40
  367. nltk-3.3/nltk/translate/nist_score.py +178 -0
  368. {nltk-3.2 → nltk-3.3}/nltk/translate/phrase_based.py +175 -175
  369. {nltk-3.2 → nltk-3.3}/nltk/translate/ribes_score.py +325 -325
  370. {nltk-3.2 → nltk-3.3}/nltk/translate/stack_decoder.py +499 -495
  371. {nltk-3.2 → nltk-3.3}/nltk/tree.py +1605 -1603
  372. {nltk-3.2 → nltk-3.3}/nltk/treeprettyprinter.py +564 -564
  373. {nltk-3.2 → nltk-3.3}/nltk/treetransforms.py +309 -309
  374. {nltk-3.2 → nltk-3.3}/nltk/twitter/__init__.py +28 -28
  375. {nltk-3.2 → nltk-3.3}/nltk/twitter/api.py +141 -136
  376. {nltk-3.2 → nltk-3.3}/nltk/twitter/common.py +264 -258
  377. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitter_demo.py +288 -288
  378. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitterclient.py +530 -530
  379. {nltk-3.2 → nltk-3.3}/nltk/twitter/util.py +147 -146
  380. nltk-3.3/nltk/util.py +751 -0
  381. {nltk-3.2 → nltk-3.3}/nltk/wsd.py +53 -53
  382. {nltk-3.2 → nltk-3.3/nltk.egg-info}/PKG-INFO +13 -5
  383. {nltk-3.2 → nltk-3.3}/nltk.egg-info/SOURCES.txt +26 -3
  384. nltk-3.3/nltk.egg-info/requires.txt +31 -0
  385. {nltk-3.2 → nltk-3.3}/setup.cfg +0 -1
  386. {nltk-3.2 → nltk-3.3}/setup.py +102 -75
  387. nltk-3.2/nltk/VERSION +0 -1
  388. nltk-3.2/nltk/compat.py +0 -680
  389. nltk-3.2/nltk/corpus/reader/pl196x.py +0 -278
  390. nltk-3.2/nltk/corpus/reader/verbnet.py +0 -408
  391. nltk-3.2/nltk/corpus/reader/wordlist.py +0 -37
  392. nltk-3.2/nltk/sentiment/vader_lexicon.txt +0 -7517
  393. nltk-3.2/nltk/six.py +0 -868
  394. nltk-3.2/nltk/stem/porter.py +0 -692
  395. nltk-3.2/nltk/test/unit/test_2x_compat.py +0 -26
  396. nltk-3.2/nltk/test/unit/test_stem.py +0 -36
  397. nltk-3.2/nltk/test/unit/test_tokenize.py +0 -23
  398. nltk-3.2/nltk/test/unit/translate/test_bleu.py +0 -84
  399. nltk-3.2/nltk/tokenize/treebank.py +0 -124
  400. nltk-3.2/nltk/tokenize/util.py +0 -90
  401. nltk-3.2/nltk/util.py +0 -1364
  402. {nltk-3.2 → nltk-3.3}/nltk/test/unit/__init__.py +0 -0
  403. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/__init__.py +0 -0
  404. {nltk-3.2 → nltk-3.3}/nltk.egg-info/dependency_links.txt +0 -0
  405. {nltk-3.2 → nltk-3.3}/nltk.egg-info/not-zip-safe +0 -0
  406. {nltk-3.2 → nltk-3.3}/nltk.egg-info/top_level.txt +0 -0
@@ -1,1451 +1,1487 @@
1
- # Natural Language Toolkit: Utility functions
2
- #
3
- # Copyright (C) 2001-2015 NLTK Project
4
- # Author: Edward Loper <edloper@gmail.com>
5
- # URL: <http://nltk.org/>
6
- # For license information, see LICENSE.TXT
7
-
8
- """
9
- Functions to find and load NLTK resource files, such as corpora,
10
- grammars, and saved processing objects. Resource files are identified
11
- using URLs, such as ``nltk:corpora/abc/rural.txt`` or
12
- ``http://nltk.org/sample/toy.cfg``. The following URL protocols are
13
- supported:
14
-
15
- - ``file:path``: Specifies the file whose path is *path*.
16
- Both relative and absolute paths may be used.
17
-
18
- - ``http://host/path``: Specifies the file stored on the web
19
- server *host* at path *path*.
20
-
21
- - ``nltk:path``: Specifies the file stored in the NLTK data
22
- package at *path*. NLTK will search for these files in the
23
- directories specified by ``nltk.data.path``.
24
-
25
- If no protocol is specified, then the default protocol ``nltk:`` will
26
- be used.
27
-
28
- This module provides to functions that can be used to access a
29
- resource file, given its URL: ``load()`` loads a given resource, and
30
- adds it to a resource cache; and ``retrieve()`` copies a given resource
31
- to a local file.
32
- """
33
- from __future__ import print_function, unicode_literals
34
- from __future__ import division
35
-
36
- import sys
37
- import io
38
- import os
39
- import textwrap
40
- import re
41
- import zipfile
42
- import codecs
43
-
44
- from gzip import GzipFile, READ as GZ_READ, WRITE as GZ_WRITE
45
-
46
- try:
47
- from zlib import Z_SYNC_FLUSH as FLUSH
48
- except ImportError:
49
- from zlib import Z_FINISH as FLUSH
50
-
51
- try:
52
- import cPickle as pickle
53
- except ImportError:
54
- import pickle
55
-
56
- # this import should be more specific:
57
- import nltk
58
-
59
- from nltk.compat import py3_data, add_py3_data
60
- from nltk.compat import text_type, string_types, BytesIO, urlopen, url2pathname
61
-
62
-
63
- ######################################################################
64
- # Search Path
65
- ######################################################################
66
-
67
- path = []
68
- """A list of directories where the NLTK data package might reside.
69
- These directories will be checked in order when looking for a
70
- resource in the data package. Note that this allows users to
71
- substitute in their own versions of resources, if they have them
72
- (e.g., in their home directory under ~/nltk_data)."""
73
-
74
- # User-specified locations:
75
- _paths_from_env = os.environ.get('NLTK_DATA', str('')).split(os.pathsep)
76
- path += [d for d in _paths_from_env if d]
77
- if 'APPENGINE_RUNTIME' not in os.environ and os.path.expanduser('~/') != '~/':
78
- path.append(os.path.expanduser(str('~/nltk_data')))
79
-
80
- if sys.platform.startswith('win'):
81
- # Common locations on Windows:
82
- path += [
83
- str(r'C:\nltk_data'), str(r'D:\nltk_data'), str(r'E:\nltk_data'),
84
- os.path.join(sys.prefix, str('nltk_data')),
85
- os.path.join(sys.prefix, str('lib'), str('nltk_data')),
86
- os.path.join(
87
- os.environ.get(str('APPDATA'), str('C:\\')), str('nltk_data'))
88
- ]
89
- else:
90
- # Common locations on UNIX & OS X:
91
- path += [
92
- str('/usr/share/nltk_data'),
93
- str('/usr/local/share/nltk_data'),
94
- str('/usr/lib/nltk_data'),
95
- str('/usr/local/lib/nltk_data')
96
- ]
97
-
98
-
99
- ######################################################################
100
- # Util Functions
101
- ######################################################################
102
-
103
- def gzip_open_unicode(filename, mode="rb", compresslevel=9,
104
- encoding='utf-8', fileobj=None, errors=None, newline=None):
105
- if fileobj is None:
106
- fileobj = GzipFile(filename, mode, compresslevel, fileobj)
107
- return io.TextIOWrapper(fileobj, encoding, errors, newline)
108
-
109
-
110
- def split_resource_url(resource_url):
111
- """
112
- Splits a resource url into "<protocol>:<path>".
113
-
114
- >>> windows = sys.platform.startswith('win')
115
- >>> split_resource_url('nltk:home/nltk')
116
- ('nltk', 'home/nltk')
117
- >>> split_resource_url('nltk:/home/nltk')
118
- ('nltk', '/home/nltk')
119
- >>> split_resource_url('file:/home/nltk')
120
- ('file', '/home/nltk')
121
- >>> split_resource_url('file:///home/nltk')
122
- ('file', '/home/nltk')
123
- >>> split_resource_url('file:///C:/home/nltk')
124
- ('file', '/C:/home/nltk')
125
- """
126
- protocol, path_ = resource_url.split(':', 1)
127
- if protocol == 'nltk':
128
- pass
129
- elif protocol == 'file':
130
- if path_.startswith('/'):
131
- path_ = '/' + path_.lstrip('/')
132
- else:
133
- path_ = re.sub(r'^/{0,2}', '', path_)
134
- return protocol, path_
135
-
136
-
137
- def normalize_resource_url(resource_url):
138
- r"""
139
- Normalizes a resource url
140
-
141
- >>> windows = sys.platform.startswith('win')
142
- >>> os.path.normpath(split_resource_url(normalize_resource_url('file:grammar.fcfg'))[1]) == \
143
- ... ('\\' if windows else '') + os.path.abspath(os.path.join(os.curdir, 'grammar.fcfg'))
144
- True
145
- >>> not windows or normalize_resource_url('file:C:/dir/file') == 'file:///C:/dir/file'
146
- True
147
- >>> not windows or normalize_resource_url('file:C:\\dir\\file') == 'file:///C:/dir/file'
148
- True
149
- >>> not windows or normalize_resource_url('file:C:\\dir/file') == 'file:///C:/dir/file'
150
- True
151
- >>> not windows or normalize_resource_url('file://C:/dir/file') == 'file:///C:/dir/file'
152
- True
153
- >>> not windows or normalize_resource_url('file:////C:/dir/file') == 'file:///C:/dir/file'
154
- True
155
- >>> not windows or normalize_resource_url('nltk:C:/dir/file') == 'file:///C:/dir/file'
156
- True
157
- >>> not windows or normalize_resource_url('nltk:C:\\dir\\file') == 'file:///C:/dir/file'
158
- True
159
- >>> windows or normalize_resource_url('file:/dir/file/toy.cfg') == 'file:///dir/file/toy.cfg'
160
- True
161
- >>> normalize_resource_url('nltk:home/nltk')
162
- 'nltk:home/nltk'
163
- >>> windows or normalize_resource_url('nltk:/home/nltk') == 'file:///home/nltk'
164
- True
165
- >>> normalize_resource_url('http://example.com/dir/file')
166
- 'http://example.com/dir/file'
167
- >>> normalize_resource_url('dir/file')
168
- 'nltk:dir/file'
169
- """
170
- try:
171
- protocol, name = split_resource_url(resource_url)
172
- except ValueError:
173
- # the resource url has no protocol, use the nltk protocol by default
174
- protocol = 'nltk'
175
- name = resource_url
176
- # use file protocol if the path is an absolute path
177
- if protocol == 'nltk' and os.path.isabs(name):
178
- protocol = 'file://'
179
- name = normalize_resource_name(name, False, None)
180
- elif protocol == 'file':
181
- protocol = 'file://'
182
- # name is absolute
183
- name = normalize_resource_name(name, False, None)
184
- elif protocol == 'nltk':
185
- protocol = 'nltk:'
186
- name = normalize_resource_name(name, True)
187
- else:
188
- # handled by urllib
189
- protocol += '://'
190
- return ''.join([protocol, name])
191
-
192
-
193
- def normalize_resource_name(resource_name, allow_relative=True, relative_path=None):
194
- """
195
- :type resource_name: str or unicode
196
- :param resource_name: The name of the resource to search for.
197
- Resource names are posix-style relative path names, such as
198
- ``corpora/brown``. Directory names will automatically
199
- be converted to a platform-appropriate path separator.
200
- Directory trailing slashes are preserved
201
-
202
- >>> windows = sys.platform.startswith('win')
203
- >>> normalize_resource_name('.', True)
204
- './'
205
- >>> normalize_resource_name('./', True)
206
- './'
207
- >>> windows or normalize_resource_name('dir/file', False, '/') == '/dir/file'
208
- True
209
- >>> not windows or normalize_resource_name('C:/file', False, '/') == '/C:/file'
210
- True
211
- >>> windows or normalize_resource_name('/dir/file', False, '/') == '/dir/file'
212
- True
213
- >>> windows or normalize_resource_name('../dir/file', False, '/') == '/dir/file'
214
- True
215
- >>> not windows or normalize_resource_name('/dir/file', True, '/') == 'dir/file'
216
- True
217
- >>> windows or normalize_resource_name('/dir/file', True, '/') == '/dir/file'
218
- True
219
- """
220
- is_dir = bool(re.search(r'[\\/.]$', resource_name)) or resource_name.endswith(os.path.sep)
221
- if sys.platform.startswith('win'):
222
- resource_name = resource_name.lstrip('/')
223
- else:
224
- resource_name = re.sub(r'^/+', '/', resource_name)
225
- if allow_relative:
226
- resource_name = os.path.normpath(resource_name)
227
- else:
228
- if relative_path is None:
229
- relative_path = os.curdir
230
- resource_name = os.path.abspath(
231
- os.path.join(relative_path, resource_name))
232
- resource_name = resource_name.replace('\\', '/').replace(os.path.sep, '/')
233
- if sys.platform.startswith('win') and os.path.isabs(resource_name):
234
- resource_name = '/' + resource_name
235
- if is_dir and not resource_name.endswith('/'):
236
- resource_name += '/'
237
- return resource_name
238
-
239
-
240
- ######################################################################
241
- # Path Pointers
242
- ######################################################################
243
-
244
- class PathPointer(object):
245
- """
246
- An abstract base class for 'path pointers,' used by NLTK's data
247
- package to identify specific paths. Two subclasses exist:
248
- ``FileSystemPathPointer`` identifies a file that can be accessed
249
- directly via a given absolute path. ``ZipFilePathPointer``
250
- identifies a file contained within a zipfile, that can be accessed
251
- by reading that zipfile.
252
- """
253
-
254
- def open(self, encoding=None):
255
- """
256
- Return a seekable read-only stream that can be used to read
257
- the contents of the file identified by this path pointer.
258
-
259
- :raise IOError: If the path specified by this pointer does
260
- not contain a readable file.
261
- """
262
- raise NotImplementedError('abstract base class')
263
-
264
- def file_size(self):
265
- """
266
- Return the size of the file pointed to by this path pointer,
267
- in bytes.
268
-
269
- :raise IOError: If the path specified by this pointer does
270
- not contain a readable file.
271
- """
272
- raise NotImplementedError('abstract base class')
273
-
274
- def join(self, fileid):
275
- """
276
- Return a new path pointer formed by starting at the path
277
- identified by this pointer, and then following the relative
278
- path given by ``fileid``. The path components of ``fileid``
279
- should be separated by forward slashes, regardless of
280
- the underlying file system's path seperator character.
281
- """
282
- raise NotImplementedError('abstract base class')
283
-
284
-
285
- class FileSystemPathPointer(PathPointer, text_type):
286
- """
287
- A path pointer that identifies a file which can be accessed
288
- directly via a given absolute path.
289
- """
290
- @py3_data
291
- def __init__(self, _path):
292
- """
293
- Create a new path pointer for the given absolute path.
294
-
295
- :raise IOError: If the given path does not exist.
296
- """
297
-
298
- _path = os.path.abspath(_path)
299
- if not os.path.exists(_path):
300
- raise IOError('No such file or directory: %r' % _path)
301
- self._path = _path
302
-
303
- # There's no need to call str.__init__(), since it's a no-op;
304
- # str does all of its setup work in __new__.
305
-
306
- @property
307
- def path(self):
308
- """The absolute path identified by this path pointer."""
309
- return self._path
310
-
311
- def open(self, encoding=None):
312
- stream = open(self._path, 'rb')
313
- if encoding is not None:
314
- stream = SeekableUnicodeStreamReader(stream, encoding)
315
- return stream
316
-
317
- def file_size(self):
318
- return os.stat(self._path).st_size
319
-
320
- def join(self, fileid):
321
- _path = os.path.join(self._path, fileid)
322
- return FileSystemPathPointer(_path)
323
-
324
- def __repr__(self):
325
- # This should be a byte string under Python 2.x;
326
- # we don't want transliteration here so
327
- # @python_2_unicode_compatible is not used.
328
- return str('FileSystemPathPointer(%r)' % self._path)
329
-
330
- def __str__(self):
331
- return self._path
332
-
333
-
334
- class BufferedGzipFile(GzipFile):
335
- """
336
- A ``GzipFile`` subclass that buffers calls to ``read()`` and ``write()``.
337
- This allows faster reads and writes of data to and from gzip-compressed
338
- files at the cost of using more memory.
339
-
340
- The default buffer size is 2MB.
341
-
342
- ``BufferedGzipFile`` is useful for loading large gzipped pickle objects
343
- as well as writing large encoded feature files for classifier training.
344
- """
345
- MB = 2 ** 20
346
- SIZE = 2 * MB
347
-
348
- @py3_data
349
- def __init__(self, filename=None, mode=None, compresslevel=9,
350
- fileobj=None, **kwargs):
351
- """
352
- Return a buffered gzip file object.
353
-
354
- :param filename: a filesystem path
355
- :type filename: str
356
- :param mode: a file mode which can be any of 'r', 'rb', 'a', 'ab',
357
- 'w', or 'wb'
358
- :type mode: str
359
- :param compresslevel: The compresslevel argument is an integer from 1
360
- to 9 controlling the level of compression; 1 is fastest and
361
- produces the least compression, and 9 is slowest and produces the
362
- most compression. The default is 9.
363
- :type compresslevel: int
364
- :param fileobj: a BytesIO stream to read from instead of a file.
365
- :type fileobj: BytesIO
366
- :param size: number of bytes to buffer during calls to read() and write()
367
- :type size: int
368
- :rtype: BufferedGzipFile
369
- """
370
- GzipFile.__init__(self, filename, mode, compresslevel, fileobj)
371
- self._size = kwargs.get('size', self.SIZE)
372
- # Note: In > Python3.5, GzipFile is already using a
373
- # buffered reader in the backend which has a variable self._buffer
374
- # See https://github.com/nltk/nltk/issues/1308
375
- if sys.version.startswith('3.5'):
376
- sys.stderr.write("Use the native Python gzip.GzipFile instead.")
377
- self._nltk_buffer = BytesIO()
378
- # cStringIO does not support len.
379
- self._len = 0
380
-
381
- def _reset_buffer(self):
382
- # For some reason calling BytesIO.truncate() here will lead to
383
- # inconsistent writes so just set _buffer to a new BytesIO object.
384
- self._nltk_buffer = BytesIO()
385
- self._len = 0
386
-
387
- def _write_buffer(self, data):
388
- # Simply write to the buffer and increment the buffer size.
389
- if data is not None:
390
- self._nltk_buffer.write(data)
391
- self._len += len(data)
392
-
393
- def _write_gzip(self, data):
394
- # Write the current buffer to the GzipFile.
395
- GzipFile.write(self, self._nltk_buffer.getvalue())
396
- # Then reset the buffer and write the new data to the buffer.
397
- self._reset_buffer()
398
- self._write_buffer(data)
399
-
400
- def close(self):
401
- # GzipFile.close() doesn't actuallly close anything.
402
- if self.mode == GZ_WRITE:
403
- self._write_gzip(None)
404
- self._reset_buffer()
405
- return GzipFile.close(self)
406
-
407
- def flush(self, lib_mode=FLUSH):
408
- self._nltk_buffer.flush()
409
- GzipFile.flush(self, lib_mode)
410
-
411
- def read(self, size=None):
412
- if not size:
413
- size = self._size
414
- contents = BytesIO()
415
- while True:
416
- blocks = GzipFile.read(self, size)
417
- if not blocks:
418
- contents.flush()
419
- break
420
- contents.write(blocks)
421
- return contents.getvalue()
422
- else:
423
- return GzipFile.read(self, size)
424
-
425
- def write(self, data, size=-1):
426
- """
427
- :param data: bytes to write to file or buffer
428
- :type data: bytes
429
- :param size: buffer at least size bytes before writing to file
430
- :type size: int
431
- """
432
- if not size:
433
- size = self._size
434
- if self._len + len(data) <= size:
435
- self._write_buffer(data)
436
- else:
437
- self._write_gzip(data)
438
-
439
-
440
- class GzipFileSystemPathPointer(FileSystemPathPointer):
441
- """
442
- A subclass of ``FileSystemPathPointer`` that identifies a gzip-compressed
443
- file located at a given absolute path. ``GzipFileSystemPathPointer`` is
444
- appropriate for loading large gzip-compressed pickle objects efficiently.
445
- """
446
-
447
- def open(self, encoding=None):
448
- stream = BufferedGzipFile(self._path, 'rb')
449
- if encoding:
450
- stream = SeekableUnicodeStreamReader(stream, encoding)
451
- return stream
452
-
453
-
454
- class ZipFilePathPointer(PathPointer):
455
- """
456
- A path pointer that identifies a file contained within a zipfile,
457
- which can be accessed by reading that zipfile.
458
- """
459
- @py3_data
460
- def __init__(self, zipfile, entry=''):
461
- """
462
- Create a new path pointer pointing at the specified entry
463
- in the given zipfile.
464
-
465
- :raise IOError: If the given zipfile does not exist, or if it
466
- does not contain the specified entry.
467
- """
468
- if isinstance(zipfile, string_types):
469
- zipfile = OpenOnDemandZipFile(os.path.abspath(zipfile))
470
-
471
- # Normalize the entry string, it should be relative:
472
- entry = normalize_resource_name(entry, True, '/').lstrip('/')
473
-
474
- # Check that the entry exists:
475
- if entry:
476
- try:
477
- zipfile.getinfo(entry)
478
- except Exception:
479
- # Sometimes directories aren't explicitly listed in
480
- # the zip file. So if `entry` is a directory name,
481
- # then check if the zipfile contains any files that
482
- # are under the given directory.
483
- if (entry.endswith('/') and
484
- [n for n in zipfile.namelist() if n.startswith(entry)]):
485
- pass # zipfile contains a file in that directory.
486
- else:
487
- # Otherwise, complain.
488
- raise IOError('Zipfile %r does not contain %r' %
489
- (zipfile.filename, entry))
490
- self._zipfile = zipfile
491
- self._entry = entry
492
-
493
- @property
494
- def zipfile(self):
495
- """
496
- The zipfile.ZipFile object used to access the zip file
497
- containing the entry identified by this path pointer.
498
- """
499
- return self._zipfile
500
-
501
- @property
502
- def entry(self):
503
- """
504
- The name of the file within zipfile that this path
505
- pointer points to.
506
- """
507
- return self._entry
508
-
509
- def open(self, encoding=None):
510
- data = self._zipfile.read(self._entry)
511
- stream = BytesIO(data)
512
- if self._entry.endswith('.gz'):
513
- stream = BufferedGzipFile(self._entry, fileobj=stream)
514
- elif encoding is not None:
515
- stream = SeekableUnicodeStreamReader(stream, encoding)
516
- return stream
517
-
518
- def file_size(self):
519
- return self._zipfile.getinfo(self._entry).file_size
520
-
521
- def join(self, fileid):
522
- entry = '%s/%s' % (self._entry, fileid)
523
- return ZipFilePathPointer(self._zipfile, entry)
524
-
525
- def __repr__(self):
526
- return str('ZipFilePathPointer(%r, %r)') % (
527
- self._zipfile.filename, self._entry)
528
-
529
- def __str__(self):
530
- return os.path.normpath(os.path.join(self._zipfile.filename, self._entry))
531
-
532
- ######################################################################
533
- # Access Functions
534
- ######################################################################
535
-
536
- # Don't use a weak dictionary, because in the common case this
537
- # causes a lot more reloading that necessary.
538
- _resource_cache = {}
539
- """A dictionary used to cache resources so that they won't
540
- need to be loaded more than once."""
541
-
542
-
543
- def find(resource_name, paths=None):
544
- """
545
- Find the given resource by searching through the directories and
546
- zip files in paths, where a None or empty string specifies an absolute path.
547
- Returns a corresponding path name. If the given resource is not
548
- found, raise a ``LookupError``, whose message gives a pointer to
549
- the installation instructions for the NLTK downloader.
550
-
551
- Zip File Handling:
552
-
553
- - If ``resource_name`` contains a component with a ``.zip``
554
- extension, then it is assumed to be a zipfile; and the
555
- remaining path components are used to look inside the zipfile.
556
-
557
- - If any element of ``nltk.data.path`` has a ``.zip`` extension,
558
- then it is assumed to be a zipfile.
559
-
560
- - If a given resource name that does not contain any zipfile
561
- component is not found initially, then ``find()`` will make a
562
- second attempt to find that resource, by replacing each
563
- component *p* in the path with *p.zip/p*. For example, this
564
- allows ``find()`` to map the resource name
565
- ``corpora/chat80/cities.pl`` to a zip file path pointer to
566
- ``corpora/chat80.zip/chat80/cities.pl``.
567
-
568
- - When using ``find()`` to locate a directory contained in a
569
- zipfile, the resource name must end with the forward slash
570
- character. Otherwise, ``find()`` will not locate the
571
- directory.
572
-
573
- :type resource_name: str or unicode
574
- :param resource_name: The name of the resource to search for.
575
- Resource names are posix-style relative path names, such as
576
- ``corpora/brown``. Directory names will be
577
- automatically converted to a platform-appropriate path separator.
578
- :rtype: str
579
- """
580
- resource_name = normalize_resource_name(resource_name, True)
581
-
582
- # Resolve default paths at runtime in-case the user overrides
583
- # nltk.data.path
584
- if paths is None:
585
- paths = path
586
-
587
- # Check if the resource name includes a zipfile name
588
- m = re.match(r'(.*\.zip)/?(.*)$|', resource_name)
589
- zipfile, zipentry = m.groups()
590
-
591
- # Check each item in our path
592
- for path_ in paths:
593
- # Is the path item a zipfile?
594
- if path_ and (os.path.isfile(path_) and path_.endswith('.zip')):
595
- try:
596
- return ZipFilePathPointer(path_, resource_name)
597
- except IOError:
598
- # resource not in zipfile
599
- continue
600
-
601
- # Is the path item a directory or is resource_name an absolute path?
602
- elif not path_ or os.path.isdir(path_):
603
- if zipfile is None:
604
- p = os.path.join(path_, url2pathname(resource_name))
605
- if os.path.exists(p):
606
- if p.endswith('.gz'):
607
- return GzipFileSystemPathPointer(p)
608
- else:
609
- return FileSystemPathPointer(p)
610
- else:
611
- p = os.path.join(path_, url2pathname(zipfile))
612
- if os.path.exists(p):
613
- try:
614
- return ZipFilePathPointer(p, zipentry)
615
- except IOError:
616
- # resource not in zipfile
617
- continue
618
-
619
- # Fallback: if the path doesn't include a zip file, then try
620
- # again, assuming that one of the path components is inside a
621
- # zipfile of the same name.
622
- if zipfile is None:
623
- pieces = resource_name.split('/')
624
- for i in range(len(pieces)):
625
- modified_name = '/'.join(pieces[:i] +
626
- [pieces[i] + '.zip'] + pieces[i:])
627
- try:
628
- return find(modified_name, paths)
629
- except LookupError:
630
- pass
631
-
632
- # Display a friendly error message if the resource wasn't found:
633
- msg = textwrap.fill(
634
- 'Resource %r not found. Please use the NLTK Downloader to '
635
- 'obtain the resource: >>> nltk.download()' %
636
- (resource_name,), initial_indent=' ', subsequent_indent=' ',
637
- width=66)
638
- msg += '\n Searched in:' + ''.join('\n - %r' % d for d in paths)
639
- sep = '*' * 70
640
- resource_not_found = '\n%s\n%s\n%s' % (sep, msg, sep)
641
- raise LookupError(resource_not_found)
642
-
643
-
644
- def retrieve(resource_url, filename=None, verbose=True):
645
- """
646
- Copy the given resource to a local file. If no filename is
647
- specified, then use the URL's filename. If there is already a
648
- file named ``filename``, then raise a ``ValueError``.
649
-
650
- :type resource_url: str
651
- :param resource_url: A URL specifying where the resource should be
652
- loaded from. The default protocol is "nltk:", which searches
653
- for the file in the the NLTK data package.
654
- """
655
- resource_url = normalize_resource_url(resource_url)
656
- if filename is None:
657
- if resource_url.startswith('file:'):
658
- filename = os.path.split(resource_url)[-1]
659
- else:
660
- filename = re.sub(r'(^\w+:)?.*/', '', resource_url)
661
- if os.path.exists(filename):
662
- filename = os.path.abspath(filename)
663
- raise ValueError("File %r already exists!" % filename)
664
-
665
- if verbose:
666
- print('Retrieving %r, saving to %r' % (resource_url, filename))
667
-
668
- # Open the input & output streams.
669
- infile = _open(resource_url)
670
-
671
- # Copy infile -> outfile, using 64k blocks.
672
- with open(filename, "wb") as outfile:
673
- while True:
674
- s = infile.read(1024 * 64) # 64k blocks.
675
- outfile.write(s)
676
- if not s:
677
- break
678
-
679
- infile.close()
680
-
681
- #: A dictionary describing the formats that are supported by NLTK's
682
- #: load() method. Keys are format names, and values are format
683
- #: descriptions.
684
- FORMATS = {
685
- 'pickle': "A serialized python object, stored using the pickle module.",
686
- 'json': "A serialized python object, stored using the json module.",
687
- 'yaml': "A serialized python object, stored using the yaml module.",
688
- 'cfg': "A context free grammar.",
689
- 'pcfg': "A probabilistic CFG.",
690
- 'fcfg': "A feature CFG.",
691
- 'fol': "A list of first order logic expressions, parsed with "
692
- "nltk.sem.logic.Expression.fromstring.",
693
- 'logic': "A list of first order logic expressions, parsed with "
694
- "nltk.sem.logic.LogicParser. Requires an additional logic_parser "
695
- "parameter",
696
- 'val': "A semantic valuation, parsed by nltk.sem.Valuation.fromstring.",
697
- 'raw': "The raw (byte string) contents of a file.",
698
- 'text': "The raw (unicode string) contents of a file. "
699
- }
700
-
701
- #: A dictionary mapping from file extensions to format names, used
702
- #: by load() when format="auto" to decide the format for a
703
- #: given resource url.
704
- AUTO_FORMATS = {
705
- 'pickle': 'pickle',
706
- 'json': 'json',
707
- 'yaml': 'yaml',
708
- 'cfg': 'cfg',
709
- 'pcfg': 'pcfg',
710
- 'fcfg': 'fcfg',
711
- 'fol': 'fol',
712
- 'logic': 'logic',
713
- 'val': 'val',
714
- 'txt': 'text',
715
- 'text': 'text',
716
- }
717
-
718
-
719
- def load(resource_url, format='auto', cache=True, verbose=False,
720
- logic_parser=None, fstruct_reader=None, encoding=None):
721
- """
722
- Load a given resource from the NLTK data package. The following
723
- resource formats are currently supported:
724
-
725
- - ``pickle``
726
- - ``json``
727
- - ``yaml``
728
- - ``cfg`` (context free grammars)
729
- - ``pcfg`` (probabilistic CFGs)
730
- - ``fcfg`` (feature-based CFGs)
731
- - ``fol`` (formulas of First Order Logic)
732
- - ``logic`` (Logical formulas to be parsed by the given logic_parser)
733
- - ``val`` (valuation of First Order Logic model)
734
- - ``text`` (the file contents as a unicode string)
735
- - ``raw`` (the raw file contents as a byte string)
736
-
737
- If no format is specified, ``load()`` will attempt to determine a
738
- format based on the resource name's file extension. If that
739
- fails, ``load()`` will raise a ``ValueError`` exception.
740
-
741
- For all text formats (everything except ``pickle``, ``json``, ``yaml`` and ``raw``),
742
- it tries to decode the raw contents using UTF-8, and if that doesn't
743
- work, it tries with ISO-8859-1 (Latin-1), unless the ``encoding``
744
- is specified.
745
-
746
- :type resource_url: str
747
- :param resource_url: A URL specifying where the resource should be
748
- loaded from. The default protocol is "nltk:", which searches
749
- for the file in the the NLTK data package.
750
- :type cache: bool
751
- :param cache: If true, add this resource to a cache. If load()
752
- finds a resource in its cache, then it will return it from the
753
- cache rather than loading it. The cache uses weak references,
754
- so a resource wil automatically be expunged from the cache
755
- when no more objects are using it.
756
- :type verbose: bool
757
- :param verbose: If true, print a message when loading a resource.
758
- Messages are not displayed when a resource is retrieved from
759
- the cache.
760
- :type logic_parser: LogicParser
761
- :param logic_parser: The parser that will be used to parse logical
762
- expressions.
763
- :type fstruct_reader: FeatStructReader
764
- :param fstruct_reader: The parser that will be used to parse the
765
- feature structure of an fcfg.
766
- :type encoding: str
767
- :param encoding: the encoding of the input; only used for text formats.
768
- """
769
- resource_url = normalize_resource_url(resource_url)
770
- resource_url = add_py3_data(resource_url)
771
-
772
- # Determine the format of the resource.
773
- if format == 'auto':
774
- resource_url_parts = resource_url.split('.')
775
- ext = resource_url_parts[-1]
776
- if ext == 'gz':
777
- ext = resource_url_parts[-2]
778
- format = AUTO_FORMATS.get(ext)
779
- if format is None:
780
- raise ValueError('Could not determine format for %s based '
781
- 'on its file\nextension; use the "format" '
782
- 'argument to specify the format explicitly.'
783
- % resource_url)
784
-
785
- if format not in FORMATS:
786
- raise ValueError('Unknown format type: %s!' % (format,))
787
-
788
- # If we've cached the resource, then just return it.
789
- if cache:
790
- resource_val = _resource_cache.get((resource_url, format))
791
- if resource_val is not None:
792
- if verbose:
793
- print('<<Using cached copy of %s>>' % (resource_url,))
794
- return resource_val
795
-
796
- # Let the user know what's going on.
797
- if verbose:
798
- print('<<Loading %s>>' % (resource_url,))
799
-
800
- # Load the resource.
801
- opened_resource = _open(resource_url)
802
-
803
- if format == 'raw':
804
- resource_val = opened_resource.read()
805
- elif format == 'pickle':
806
- resource_val = pickle.load(opened_resource)
807
- elif format == 'json':
808
- import json
809
- from nltk.jsontags import json_tags
810
- resource_val = json.load(opened_resource)
811
- tag = None
812
- if len(resource_val) != 1:
813
- tag = next(resource_val.keys())
814
- if tag not in json_tags:
815
- raise ValueError('Unknown json tag.')
816
- elif format == 'yaml':
817
- import yaml
818
- resource_val = yaml.load(opened_resource)
819
- else:
820
- # The resource is a text format.
821
- binary_data = opened_resource.read()
822
- if encoding is not None:
823
- string_data = binary_data.decode(encoding)
824
- else:
825
- try:
826
- string_data = binary_data.decode('utf-8')
827
- except UnicodeDecodeError:
828
- string_data = binary_data.decode('latin-1')
829
- if format == 'text':
830
- resource_val = string_data
831
- elif format == 'cfg':
832
- resource_val = nltk.grammar.CFG.fromstring(
833
- string_data, encoding=encoding)
834
- elif format == 'pcfg':
835
- resource_val = nltk.grammar.PCFG.fromstring(
836
- string_data, encoding=encoding)
837
- elif format == 'fcfg':
838
- resource_val = nltk.grammar.FeatureGrammar.fromstring(
839
- string_data, logic_parser=logic_parser,
840
- fstruct_reader=fstruct_reader, encoding=encoding)
841
- elif format == 'fol':
842
- resource_val = nltk.sem.read_logic(
843
- string_data, logic_parser=nltk.sem.logic.LogicParser(),
844
- encoding=encoding)
845
- elif format == 'logic':
846
- resource_val = nltk.sem.read_logic(
847
- string_data, logic_parser=logic_parser, encoding=encoding)
848
- elif format == 'val':
849
- resource_val = nltk.sem.read_valuation(
850
- string_data, encoding=encoding)
851
- else:
852
- raise AssertionError("Internal NLTK error: Format %s isn't "
853
- "handled by nltk.data.load()" % (format,))
854
-
855
- opened_resource.close()
856
-
857
- # If requested, add it to the cache.
858
- if cache:
859
- try:
860
- _resource_cache[(resource_url, format)] = resource_val
861
- # TODO: add this line
862
- # print('<<Caching a copy of %s>>' % (resource_url,))
863
- except TypeError:
864
- # We can't create weak references to some object types, like
865
- # strings and tuples. For now, just don't cache them.
866
- pass
867
-
868
- return resource_val
869
-
870
-
871
- def show_cfg(resource_url, escape='##'):
872
- """
873
- Write out a grammar file, ignoring escaped and empty lines.
874
-
875
- :type resource_url: str
876
- :param resource_url: A URL specifying where the resource should be
877
- loaded from. The default protocol is "nltk:", which searches
878
- for the file in the the NLTK data package.
879
- :type escape: str
880
- :param escape: Prepended string that signals lines to be ignored
881
- """
882
- resource_url = normalize_resource_url(resource_url)
883
- resource_val = load(resource_url, format='text', cache=False)
884
- lines = resource_val.splitlines()
885
- for l in lines:
886
- if l.startswith(escape):
887
- continue
888
- if re.match('^$', l):
889
- continue
890
- print(l)
891
-
892
-
893
- def clear_cache():
894
- """
895
- Remove all objects from the resource cache.
896
- :see: load()
897
- """
898
- _resource_cache.clear()
899
-
900
-
901
- def _open(resource_url):
902
- """
903
- Helper function that returns an open file object for a resource,
904
- given its resource URL. If the given resource URL uses the "nltk:"
905
- protocol, or uses no protocol, then use ``nltk.data.find`` to find
906
- its path, and open it with the given mode; if the resource URL
907
- uses the 'file' protocol, then open the file with the given mode;
908
- otherwise, delegate to ``urllib2.urlopen``.
909
-
910
- :type resource_url: str
911
- :param resource_url: A URL specifying where the resource should be
912
- loaded from. The default protocol is "nltk:", which searches
913
- for the file in the the NLTK data package.
914
- """
915
- resource_url = normalize_resource_url(resource_url)
916
- protocol, path_ = split_resource_url(resource_url)
917
-
918
- if protocol is None or protocol.lower() == 'nltk':
919
- return find(path_, path + ['']).open()
920
- elif protocol.lower() == 'file':
921
- # urllib might not use mode='rb', so handle this one ourselves:
922
- return find(path_, ['']).open()
923
- else:
924
- return urlopen(resource_url)
925
-
926
- ######################################################################
927
- # Lazy Resource Loader
928
- ######################################################################
929
-
930
- # We shouldn't apply @python_2_unicode_compatible
931
- # decorator to LazyLoader, this is resource.__class__ responsibility.
932
-
933
-
934
- class LazyLoader(object):
935
-
936
- @py3_data
937
- def __init__(self, _path):
938
- self._path = _path
939
-
940
- def __load(self):
941
- resource = load(self._path)
942
- # This is where the magic happens! Transform ourselves into
943
- # the object by modifying our own __dict__ and __class__ to
944
- # match that of `resource`.
945
- self.__dict__ = resource.__dict__
946
- self.__class__ = resource.__class__
947
-
948
- def __getattr__(self, attr):
949
- self.__load()
950
- # This looks circular, but its not, since __load() changes our
951
- # __class__ to something new:
952
- return getattr(self, attr)
953
-
954
- def __repr__(self):
955
- self.__load()
956
- # This looks circular, but its not, since __load() changes our
957
- # __class__ to something new:
958
- return repr(self)
959
-
960
- ######################################################################
961
- # Open-On-Demand ZipFile
962
- ######################################################################
963
-
964
-
965
- class OpenOnDemandZipFile(zipfile.ZipFile):
966
- """
967
- A subclass of ``zipfile.ZipFile`` that closes its file pointer
968
- whenever it is not using it; and re-opens it when it needs to read
969
- data from the zipfile. This is useful for reducing the number of
970
- open file handles when many zip files are being accessed at once.
971
- ``OpenOnDemandZipFile`` must be constructed from a filename, not a
972
- file-like object (to allow re-opening). ``OpenOnDemandZipFile`` is
973
- read-only (i.e. ``write()`` and ``writestr()`` are disabled.
974
- """
975
- @py3_data
976
- def __init__(self, filename):
977
- if not isinstance(filename, string_types):
978
- raise TypeError('ReopenableZipFile filename must be a string')
979
- zipfile.ZipFile.__init__(self, filename)
980
- assert self.filename == filename
981
- self.close()
982
- # After closing a ZipFile object, the _fileRefCnt needs to be cleared
983
- # for Python2and3 compatible code.
984
- self._fileRefCnt = 0
985
-
986
- def read(self, name):
987
- assert self.fp is None
988
- self.fp = open(self.filename, 'rb')
989
- value = zipfile.ZipFile.read(self, name)
990
- # Ensure that _fileRefCnt needs to be set for Python2and3 compatible code.
991
- # Since we only opened one file here, we add 1.
992
- self._fileRefCnt += 1
993
- self.close()
994
- return value
995
-
996
- def write(self, *args, **kwargs):
997
- """:raise NotImplementedError: OpenOnDemandZipfile is read-only"""
998
- raise NotImplementedError('OpenOnDemandZipfile is read-only')
999
-
1000
- def writestr(self, *args, **kwargs):
1001
- """:raise NotImplementedError: OpenOnDemandZipfile is read-only"""
1002
- raise NotImplementedError('OpenOnDemandZipfile is read-only')
1003
-
1004
- def __repr__(self):
1005
- return repr(str('OpenOnDemandZipFile(%r)') % self.filename)
1006
-
1007
- ######################################################################
1008
- #{ Seekable Unicode Stream Reader
1009
- ######################################################################
1010
-
1011
-
1012
- class SeekableUnicodeStreamReader(object):
1013
- """
1014
- A stream reader that automatically encodes the source byte stream
1015
- into unicode (like ``codecs.StreamReader``); but still supports the
1016
- ``seek()`` and ``tell()`` operations correctly. This is in contrast
1017
- to ``codecs.StreamReader``, which provide *broken* ``seek()`` and
1018
- ``tell()`` methods.
1019
-
1020
- This class was motivated by ``StreamBackedCorpusView``, which
1021
- makes extensive use of ``seek()`` and ``tell()``, and needs to be
1022
- able to handle unicode-encoded files.
1023
-
1024
- Note: this class requires stateless decoders. To my knowledge,
1025
- this shouldn't cause a problem with any of python's builtin
1026
- unicode encodings.
1027
- """
1028
- DEBUG = True # : If true, then perform extra sanity checks.
1029
-
1030
- @py3_data
1031
- def __init__(self, stream, encoding, errors='strict'):
1032
- # Rewind the stream to its beginning.
1033
- stream.seek(0)
1034
-
1035
- self.stream = stream
1036
- """The underlying stream."""
1037
-
1038
- self.encoding = encoding
1039
- """The name of the encoding that should be used to encode the
1040
- underlying stream."""
1041
-
1042
- self.errors = errors
1043
- """The error mode that should be used when decoding data from
1044
- the underlying stream. Can be 'strict', 'ignore', or
1045
- 'replace'."""
1046
-
1047
- self.decode = codecs.getdecoder(encoding)
1048
- """The function that is used to decode byte strings into
1049
- unicode strings."""
1050
-
1051
- self.bytebuffer = b''
1052
- """A buffer to use bytes that have been read but have not yet
1053
- been decoded. This is only used when the final bytes from
1054
- a read do not form a complete encoding for a character."""
1055
-
1056
- self.linebuffer = None
1057
- """A buffer used by ``readline()`` to hold characters that have
1058
- been read, but have not yet been returned by ``read()`` or
1059
- ``readline()``. This buffer consists of a list of unicode
1060
- strings, where each string corresponds to a single line.
1061
- The final element of the list may or may not be a complete
1062
- line. Note that the existence of a linebuffer makes the
1063
- ``tell()`` operation more complex, because it must backtrack
1064
- to the beginning of the buffer to determine the correct
1065
- file position in the underlying byte stream."""
1066
-
1067
- self._rewind_checkpoint = 0
1068
- """The file position at which the most recent read on the
1069
- underlying stream began. This is used, together with
1070
- ``_rewind_numchars``, to backtrack to the beginning of
1071
- ``linebuffer`` (which is required by ``tell()``)."""
1072
-
1073
- self._rewind_numchars = None
1074
- """The number of characters that have been returned since the
1075
- read that started at ``_rewind_checkpoint``. This is used,
1076
- together with ``_rewind_checkpoint``, to backtrack to the
1077
- beginning of ``linebuffer`` (which is required by ``tell()``)."""
1078
-
1079
- self._bom = self._check_bom()
1080
- """The length of the byte order marker at the beginning of
1081
- the stream (or None for no byte order marker)."""
1082
-
1083
- #/////////////////////////////////////////////////////////////////
1084
- # Read methods
1085
- #/////////////////////////////////////////////////////////////////
1086
-
1087
- def read(self, size=None):
1088
- """
1089
- Read up to ``size`` bytes, decode them using this reader's
1090
- encoding, and return the resulting unicode string.
1091
-
1092
- :param size: The maximum number of bytes to read. If not
1093
- specified, then read as many bytes as possible.
1094
- :type size: int
1095
- :rtype: unicode
1096
- """
1097
- chars = self._read(size)
1098
-
1099
- # If linebuffer is not empty, then include it in the result
1100
- if self.linebuffer:
1101
- chars = ''.join(self.linebuffer) + chars
1102
- self.linebuffer = None
1103
- self._rewind_numchars = None
1104
-
1105
- return chars
1106
-
1107
- def readline(self, size=None):
1108
- """
1109
- Read a line of text, decode it using this reader's encoding,
1110
- and return the resulting unicode string.
1111
-
1112
- :param size: The maximum number of bytes to read. If no
1113
- newline is encountered before ``size`` bytes have been read,
1114
- then the returned value may not be a complete line of text.
1115
- :type size: int
1116
- """
1117
- # If we have a non-empty linebuffer, then return the first
1118
- # line from it. (Note that the last element of linebuffer may
1119
- # not be a complete line; so let _read() deal with it.)
1120
- if self.linebuffer and len(self.linebuffer) > 1:
1121
- line = self.linebuffer.pop(0)
1122
- self._rewind_numchars += len(line)
1123
- return line
1124
-
1125
- readsize = size or 72
1126
- chars = ''
1127
-
1128
- # If there's a remaining incomplete line in the buffer, add it.
1129
- if self.linebuffer:
1130
- chars += self.linebuffer.pop()
1131
- self.linebuffer = None
1132
-
1133
- while True:
1134
- startpos = self.stream.tell() - len(self.bytebuffer)
1135
- new_chars = self._read(readsize)
1136
-
1137
- # If we're at a '\r', then read one extra character, since
1138
- # it might be a '\n', to get the proper line ending.
1139
- if new_chars and new_chars.endswith('\r'):
1140
- new_chars += self._read(1)
1141
-
1142
- chars += new_chars
1143
- lines = chars.splitlines(True)
1144
- if len(lines) > 1:
1145
- line = lines[0]
1146
- self.linebuffer = lines[1:]
1147
- self._rewind_numchars = (len(new_chars) -
1148
- (len(chars) - len(line)))
1149
- self._rewind_checkpoint = startpos
1150
- break
1151
- elif len(lines) == 1:
1152
- line0withend = lines[0]
1153
- line0withoutend = lines[0].splitlines(False)[0]
1154
- if line0withend != line0withoutend: # complete line
1155
- line = line0withend
1156
- break
1157
-
1158
- if not new_chars or size is not None:
1159
- line = chars
1160
- break
1161
-
1162
- # Read successively larger blocks of text.
1163
- if readsize < 8000:
1164
- readsize *= 2
1165
-
1166
- return line
1167
-
1168
- def readlines(self, sizehint=None, keepends=True):
1169
- """
1170
- Read this file's contents, decode them using this reader's
1171
- encoding, and return it as a list of unicode lines.
1172
-
1173
- :rtype: list(unicode)
1174
- :param sizehint: Ignored.
1175
- :param keepends: If false, then strip newlines.
1176
- """
1177
- return self.read().splitlines(keepends)
1178
-
1179
- def next(self):
1180
- """Return the next decoded line from the underlying stream."""
1181
- line = self.readline()
1182
- if line:
1183
- return line
1184
- else:
1185
- raise StopIteration
1186
-
1187
- def __next__(self):
1188
- return self.next()
1189
-
1190
- def __iter__(self):
1191
- """Return self"""
1192
- return self
1193
-
1194
- def xreadlines(self):
1195
- """Return self"""
1196
- return self
1197
-
1198
- #/////////////////////////////////////////////////////////////////
1199
- # Pass-through methods & properties
1200
- #/////////////////////////////////////////////////////////////////
1201
-
1202
- @property
1203
- def closed(self):
1204
- """True if the underlying stream is closed."""
1205
- return self.stream.closed
1206
-
1207
- @property
1208
- def name(self):
1209
- """The name of the underlying stream."""
1210
- return self.stream.name
1211
-
1212
- @property
1213
- def mode(self):
1214
- """The mode of the underlying stream."""
1215
- return self.stream.mode
1216
-
1217
- def close(self):
1218
- """
1219
- Close the underlying stream.
1220
- """
1221
- self.stream.close()
1222
-
1223
- #/////////////////////////////////////////////////////////////////
1224
- # Seek and tell
1225
- #/////////////////////////////////////////////////////////////////
1226
-
1227
- def seek(self, offset, whence=0):
1228
- """
1229
- Move the stream to a new file position. If the reader is
1230
- maintaining any buffers, then they will be cleared.
1231
-
1232
- :param offset: A byte count offset.
1233
- :param whence: If 0, then the offset is from the start of the file
1234
- (offset should be positive), if 1, then the offset is from the
1235
- current position (offset may be positive or negative); and if 2,
1236
- then the offset is from the end of the file (offset should
1237
- typically be negative).
1238
- """
1239
- if whence == 1:
1240
- raise ValueError('Relative seek is not supported for '
1241
- 'SeekableUnicodeStreamReader -- consider '
1242
- 'using char_seek_forward() instead.')
1243
- self.stream.seek(offset, whence)
1244
- self.linebuffer = None
1245
- self.bytebuffer = b''
1246
- self._rewind_numchars = None
1247
- self._rewind_checkpoint = self.stream.tell()
1248
-
1249
- def char_seek_forward(self, offset):
1250
- """
1251
- Move the read pointer forward by ``offset`` characters.
1252
- """
1253
- if offset < 0:
1254
- raise ValueError('Negative offsets are not supported')
1255
- # Clear all buffers.
1256
- self.seek(self.tell())
1257
- # Perform the seek operation.
1258
- self._char_seek_forward(offset)
1259
-
1260
- def _char_seek_forward(self, offset, est_bytes=None):
1261
- """
1262
- Move the file position forward by ``offset`` characters,
1263
- ignoring all buffers.
1264
-
1265
- :param est_bytes: A hint, giving an estimate of the number of
1266
- bytes that will be needed to move forward by ``offset`` chars.
1267
- Defaults to ``offset``.
1268
- """
1269
- if est_bytes is None:
1270
- est_bytes = offset
1271
- bytes = b''
1272
-
1273
- while True:
1274
- # Read in a block of bytes.
1275
- newbytes = self.stream.read(est_bytes - len(bytes))
1276
- bytes += newbytes
1277
-
1278
- # Decode the bytes to characters.
1279
- chars, bytes_decoded = self._incr_decode(bytes)
1280
-
1281
- # If we got the right number of characters, then seek
1282
- # backwards over any truncated characters, and return.
1283
- if len(chars) == offset:
1284
- self.stream.seek(-len(bytes) + bytes_decoded, 1)
1285
- return
1286
-
1287
- # If we went too far, then we can back-up until we get it
1288
- # right, using the bytes we've already read.
1289
- if len(chars) > offset:
1290
- while len(chars) > offset:
1291
- # Assume at least one byte/char.
1292
- est_bytes += offset - len(chars)
1293
- chars, bytes_decoded = self._incr_decode(bytes[:est_bytes])
1294
- self.stream.seek(-len(bytes) + bytes_decoded, 1)
1295
- return
1296
-
1297
- # Otherwise, we haven't read enough bytes yet; loop again.
1298
- est_bytes += offset - len(chars)
1299
-
1300
- def tell(self):
1301
- """
1302
- Return the current file position on the underlying byte
1303
- stream. If this reader is maintaining any buffers, then the
1304
- returned file position will be the position of the beginning
1305
- of those buffers.
1306
- """
1307
- # If nothing's buffered, then just return our current filepos:
1308
- if self.linebuffer is None:
1309
- return self.stream.tell() - len(self.bytebuffer)
1310
-
1311
- # Otherwise, we'll need to backtrack the filepos until we
1312
- # reach the beginning of the buffer.
1313
-
1314
- # Store our original file position, so we can return here.
1315
- orig_filepos = self.stream.tell()
1316
-
1317
- # Calculate an estimate of where we think the newline is.
1318
- bytes_read = ((orig_filepos - len(self.bytebuffer)) -
1319
- self._rewind_checkpoint)
1320
- buf_size = sum(len(line) for line in self.linebuffer)
1321
- est_bytes = int((bytes_read * self._rewind_numchars /
1322
- (self._rewind_numchars + buf_size)))
1323
-
1324
- self.stream.seek(self._rewind_checkpoint)
1325
- self._char_seek_forward(self._rewind_numchars, est_bytes)
1326
- filepos = self.stream.tell()
1327
-
1328
- # Sanity check
1329
- if self.DEBUG:
1330
- self.stream.seek(filepos)
1331
- check1 = self._incr_decode(self.stream.read(50))[0]
1332
- check2 = ''.join(self.linebuffer)
1333
- assert check1.startswith(check2) or check2.startswith(check1)
1334
-
1335
- # Return to our original filepos (so we don't have to throw
1336
- # out our buffer.)
1337
- self.stream.seek(orig_filepos)
1338
-
1339
- # Return the calculated filepos
1340
- return filepos
1341
-
1342
- #/////////////////////////////////////////////////////////////////
1343
- # Helper methods
1344
- #/////////////////////////////////////////////////////////////////
1345
-
1346
- def _read(self, size=None):
1347
- """
1348
- Read up to ``size`` bytes from the underlying stream, decode
1349
- them using this reader's encoding, and return the resulting
1350
- unicode string. ``linebuffer`` is not included in the result.
1351
- """
1352
- if size == 0:
1353
- return ''
1354
-
1355
- # Skip past the byte order marker, if present.
1356
- if self._bom and self.stream.tell() == 0:
1357
- self.stream.read(self._bom)
1358
-
1359
- # Read the requested number of bytes.
1360
- if size is None:
1361
- new_bytes = self.stream.read()
1362
- else:
1363
- new_bytes = self.stream.read(size)
1364
- bytes = self.bytebuffer + new_bytes
1365
-
1366
- # Decode the bytes into unicode characters
1367
- chars, bytes_decoded = self._incr_decode(bytes)
1368
-
1369
- # If we got bytes but couldn't decode any, then read further.
1370
- if (size is not None) and (not chars) and (len(new_bytes) > 0):
1371
- while not chars:
1372
- new_bytes = self.stream.read(1)
1373
- if not new_bytes:
1374
- break # end of file.
1375
- bytes += new_bytes
1376
- chars, bytes_decoded = self._incr_decode(bytes)
1377
-
1378
- # Record any bytes we didn't consume.
1379
- self.bytebuffer = bytes[bytes_decoded:]
1380
-
1381
- # Return the result
1382
- return chars
1383
-
1384
- def _incr_decode(self, bytes):
1385
- """
1386
- Decode the given byte string into a unicode string, using this
1387
- reader's encoding. If an exception is encountered that
1388
- appears to be caused by a truncation error, then just decode
1389
- the byte string without the bytes that cause the trunctaion
1390
- error.
1391
-
1392
- Return a tuple ``(chars, num_consumed)``, where ``chars`` is
1393
- the decoded unicode string, and ``num_consumed`` is the
1394
- number of bytes that were consumed.
1395
- """
1396
- while True:
1397
- try:
1398
- return self.decode(bytes, 'strict')
1399
- except UnicodeDecodeError as exc:
1400
- # If the exception occurs at the end of the string,
1401
- # then assume that it's a truncation error.
1402
- if exc.end == len(bytes):
1403
- return self.decode(bytes[:exc.start], self.errors)
1404
-
1405
- # Otherwise, if we're being strict, then raise it.
1406
- elif self.errors == 'strict':
1407
- raise
1408
-
1409
- # If we're not strict, then re-process it with our
1410
- # errors setting. This *may* raise an exception.
1411
- else:
1412
- return self.decode(bytes, self.errors)
1413
-
1414
- _BOM_TABLE = {
1415
- 'utf8': [(codecs.BOM_UTF8, None)],
1416
- 'utf16': [(codecs.BOM_UTF16_LE, 'utf16-le'),
1417
- (codecs.BOM_UTF16_BE, 'utf16-be')],
1418
- 'utf16le': [(codecs.BOM_UTF16_LE, None)],
1419
- 'utf16be': [(codecs.BOM_UTF16_BE, None)],
1420
- 'utf32': [(codecs.BOM_UTF32_LE, 'utf32-le'),
1421
- (codecs.BOM_UTF32_BE, 'utf32-be')],
1422
- 'utf32le': [(codecs.BOM_UTF32_LE, None)],
1423
- 'utf32be': [(codecs.BOM_UTF32_BE, None)],
1424
- }
1425
-
1426
- def _check_bom(self):
1427
- # Normalize our encoding name
1428
- enc = re.sub('[ -]', '', self.encoding.lower())
1429
-
1430
- # Look up our encoding in the BOM table.
1431
- bom_info = self._BOM_TABLE.get(enc)
1432
-
1433
- if bom_info:
1434
- # Read a prefix, to check against the BOM(s)
1435
- bytes = self.stream.read(16)
1436
- self.stream.seek(0)
1437
-
1438
- # Check for each possible BOM.
1439
- for (bom, new_encoding) in bom_info:
1440
- if bytes.startswith(bom):
1441
- if new_encoding:
1442
- self.encoding = new_encoding
1443
- return len(bom)
1444
-
1445
- return None
1446
-
1447
- __all__ = ['path', 'PathPointer', 'FileSystemPathPointer', 'BufferedGzipFile',
1448
- 'GzipFileSystemPathPointer', 'GzipFileSystemPathPointer',
1449
- 'find', 'retrieve', 'FORMATS', 'AUTO_FORMATS', 'load',
1450
- 'show_cfg', 'clear_cache', 'LazyLoader', 'OpenOnDemandZipFile',
1451
- 'GzipFileSystemPathPointer', 'SeekableUnicodeStreamReader']
1
+ # Natural Language Toolkit: Utility functions
2
+ #
3
+ # Copyright (C) 2001-2018 NLTK Project
4
+ # Author: Edward Loper <edloper@gmail.com>
5
+ # URL: <http://nltk.org/>
6
+ # For license information, see LICENSE.TXT
7
+
8
+ """
9
+ Functions to find and load NLTK resource files, such as corpora,
10
+ grammars, and saved processing objects. Resource files are identified
11
+ using URLs, such as ``nltk:corpora/abc/rural.txt`` or
12
+ ``http://nltk.org/sample/toy.cfg``. The following URL protocols are
13
+ supported:
14
+
15
+ - ``file:path``: Specifies the file whose path is *path*.
16
+ Both relative and absolute paths may be used.
17
+
18
+ - ``http://host/path``: Specifies the file stored on the web
19
+ server *host* at path *path*.
20
+
21
+ - ``nltk:path``: Specifies the file stored in the NLTK data
22
+ package at *path*. NLTK will search for these files in the
23
+ directories specified by ``nltk.data.path``.
24
+
25
+ If no protocol is specified, then the default protocol ``nltk:`` will
26
+ be used.
27
+
28
+ This module provides to functions that can be used to access a
29
+ resource file, given its URL: ``load()`` loads a given resource, and
30
+ adds it to a resource cache; and ``retrieve()`` copies a given resource
31
+ to a local file.
32
+ """
33
+ from __future__ import print_function, unicode_literals
34
+ from __future__ import division
35
+ from abc import ABCMeta, abstractmethod
36
+ from six import add_metaclass
37
+
38
+ import functools
39
+ import textwrap
40
+ import io
41
+ import os
42
+ import re
43
+ import sys
44
+ import zipfile
45
+ import codecs
46
+
47
+ from gzip import GzipFile, READ as GZ_READ, WRITE as GZ_WRITE
48
+
49
+ try: # Python 3.
50
+ textwrap_indent = functools.partial(textwrap.indent, prefix=' ')
51
+ except AttributeError: # Python 2; indent() not available for Python2.
52
+ textwrap_fill = functools.partial(textwrap.fill,
53
+ initial_indent=' ',
54
+ subsequent_indent=' ',
55
+ replace_whitespace=False)
56
+ def textwrap_indent(text):
57
+ return '\n'.join(textwrap_fill(line) for line in text.splitlines())
58
+
59
+ try:
60
+ from zlib import Z_SYNC_FLUSH as FLUSH
61
+ except ImportError:
62
+ from zlib import Z_FINISH as FLUSH
63
+
64
+ try:
65
+ import cPickle as pickle
66
+ except ImportError:
67
+ import pickle
68
+
69
+ from six import string_types, text_type
70
+ from six.moves.urllib.request import urlopen, url2pathname
71
+
72
+ # this import should be more specific:
73
+ import nltk
74
+ from nltk.compat import py3_data, add_py3_data, BytesIO
75
+
76
+ ######################################################################
77
+ # Search Path
78
+ ######################################################################
79
+
80
+ path = []
81
+ """A list of directories where the NLTK data package might reside.
82
+ These directories will be checked in order when looking for a
83
+ resource in the data package. Note that this allows users to
84
+ substitute in their own versions of resources, if they have them
85
+ (e.g., in their home directory under ~/nltk_data)."""
86
+
87
+ # User-specified locations:
88
+ _paths_from_env = os.environ.get('NLTK_DATA', str('')).split(os.pathsep)
89
+ path += [d for d in _paths_from_env if d]
90
+ if 'APPENGINE_RUNTIME' not in os.environ and os.path.expanduser('~/') != '~/':
91
+ path.append(os.path.expanduser(str('~/nltk_data')))
92
+
93
+ if sys.platform.startswith('win'):
94
+ # Common locations on Windows:
95
+ path += [
96
+ str(r'C:\nltk_data'), str(r'D:\nltk_data'), str(r'E:\nltk_data'),
97
+ os.path.join(sys.prefix, str('nltk_data')),
98
+ os.path.join(sys.prefix, str('share'), str('nltk_data')),
99
+ os.path.join(sys.prefix, str('lib'), str('nltk_data')),
100
+ os.path.join(
101
+ os.environ.get(str('APPDATA'), str('C:\\')), str('nltk_data'))
102
+ ]
103
+ else:
104
+ # Common locations on UNIX & OS X:
105
+ path += [
106
+ str('/usr/share/nltk_data'),
107
+ str('/usr/local/share/nltk_data'),
108
+ str('/usr/lib/nltk_data'),
109
+ str('/usr/local/lib/nltk_data'),
110
+ os.path.join(sys.prefix, str('nltk_data')),
111
+ os.path.join(sys.prefix, str('share'), str('nltk_data')),
112
+ os.path.join(sys.prefix, str('lib'), str('nltk_data'))
113
+ ]
114
+
115
+
116
+ ######################################################################
117
+ # Util Functions
118
+ ######################################################################
119
+
120
+ def gzip_open_unicode(filename, mode="rb", compresslevel=9, encoding='utf-8',
121
+ fileobj=None, errors=None, newline=None):
122
+ if fileobj is None:
123
+ fileobj = GzipFile(filename, mode, compresslevel, fileobj)
124
+ return io.TextIOWrapper(fileobj, encoding, errors, newline)
125
+
126
+
127
+ def split_resource_url(resource_url):
128
+ """
129
+ Splits a resource url into "<protocol>:<path>".
130
+
131
+ >>> windows = sys.platform.startswith('win')
132
+ >>> split_resource_url('nltk:home/nltk')
133
+ ('nltk', 'home/nltk')
134
+ >>> split_resource_url('nltk:/home/nltk')
135
+ ('nltk', '/home/nltk')
136
+ >>> split_resource_url('file:/home/nltk')
137
+ ('file', '/home/nltk')
138
+ >>> split_resource_url('file:///home/nltk')
139
+ ('file', '/home/nltk')
140
+ >>> split_resource_url('file:///C:/home/nltk')
141
+ ('file', '/C:/home/nltk')
142
+ """
143
+ protocol, path_ = resource_url.split(':', 1)
144
+ if protocol == 'nltk':
145
+ pass
146
+ elif protocol == 'file':
147
+ if path_.startswith('/'):
148
+ path_ = '/' + path_.lstrip('/')
149
+ else:
150
+ path_ = re.sub(r'^/{0,2}', '', path_)
151
+ return protocol, path_
152
+
153
+
154
+ def normalize_resource_url(resource_url):
155
+ r"""
156
+ Normalizes a resource url
157
+
158
+ >>> windows = sys.platform.startswith('win')
159
+ >>> os.path.normpath(split_resource_url(normalize_resource_url('file:grammar.fcfg'))[1]) == \
160
+ ... ('\\' if windows else '') + os.path.abspath(os.path.join(os.curdir, 'grammar.fcfg'))
161
+ True
162
+ >>> not windows or normalize_resource_url('file:C:/dir/file') == 'file:///C:/dir/file'
163
+ True
164
+ >>> not windows or normalize_resource_url('file:C:\\dir\\file') == 'file:///C:/dir/file'
165
+ True
166
+ >>> not windows or normalize_resource_url('file:C:\\dir/file') == 'file:///C:/dir/file'
167
+ True
168
+ >>> not windows or normalize_resource_url('file://C:/dir/file') == 'file:///C:/dir/file'
169
+ True
170
+ >>> not windows or normalize_resource_url('file:////C:/dir/file') == 'file:///C:/dir/file'
171
+ True
172
+ >>> not windows or normalize_resource_url('nltk:C:/dir/file') == 'file:///C:/dir/file'
173
+ True
174
+ >>> not windows or normalize_resource_url('nltk:C:\\dir\\file') == 'file:///C:/dir/file'
175
+ True
176
+ >>> windows or normalize_resource_url('file:/dir/file/toy.cfg') == 'file:///dir/file/toy.cfg'
177
+ True
178
+ >>> normalize_resource_url('nltk:home/nltk')
179
+ 'nltk:home/nltk'
180
+ >>> windows or normalize_resource_url('nltk:/home/nltk') == 'file:///home/nltk'
181
+ True
182
+ >>> normalize_resource_url('http://example.com/dir/file')
183
+ 'http://example.com/dir/file'
184
+ >>> normalize_resource_url('dir/file')
185
+ 'nltk:dir/file'
186
+ """
187
+ try:
188
+ protocol, name = split_resource_url(resource_url)
189
+ except ValueError:
190
+ # the resource url has no protocol, use the nltk protocol by default
191
+ protocol = 'nltk'
192
+ name = resource_url
193
+ # use file protocol if the path is an absolute path
194
+ if protocol == 'nltk' and os.path.isabs(name):
195
+ protocol = 'file://'
196
+ name = normalize_resource_name(name, False, None)
197
+ elif protocol == 'file':
198
+ protocol = 'file://'
199
+ # name is absolute
200
+ name = normalize_resource_name(name, False, None)
201
+ elif protocol == 'nltk':
202
+ protocol = 'nltk:'
203
+ name = normalize_resource_name(name, True)
204
+ else:
205
+ # handled by urllib
206
+ protocol += '://'
207
+ return ''.join([protocol, name])
208
+
209
+
210
+ def normalize_resource_name(resource_name, allow_relative=True, relative_path=None):
211
+ """
212
+ :type resource_name: str or unicode
213
+ :param resource_name: The name of the resource to search for.
214
+ Resource names are posix-style relative path names, such as
215
+ ``corpora/brown``. Directory names will automatically
216
+ be converted to a platform-appropriate path separator.
217
+ Directory trailing slashes are preserved
218
+
219
+ >>> windows = sys.platform.startswith('win')
220
+ >>> normalize_resource_name('.', True)
221
+ './'
222
+ >>> normalize_resource_name('./', True)
223
+ './'
224
+ >>> windows or normalize_resource_name('dir/file', False, '/') == '/dir/file'
225
+ True
226
+ >>> not windows or normalize_resource_name('C:/file', False, '/') == '/C:/file'
227
+ True
228
+ >>> windows or normalize_resource_name('/dir/file', False, '/') == '/dir/file'
229
+ True
230
+ >>> windows or normalize_resource_name('../dir/file', False, '/') == '/dir/file'
231
+ True
232
+ >>> not windows or normalize_resource_name('/dir/file', True, '/') == 'dir/file'
233
+ True
234
+ >>> windows or normalize_resource_name('/dir/file', True, '/') == '/dir/file'
235
+ True
236
+ """
237
+ is_dir = bool(re.search(r'[\\/.]$', resource_name)) or resource_name.endswith(os.path.sep)
238
+ if sys.platform.startswith('win'):
239
+ resource_name = resource_name.lstrip('/')
240
+ else:
241
+ resource_name = re.sub(r'^/+', '/', resource_name)
242
+ if allow_relative:
243
+ resource_name = os.path.normpath(resource_name)
244
+ else:
245
+ if relative_path is None:
246
+ relative_path = os.curdir
247
+ resource_name = os.path.abspath(
248
+ os.path.join(relative_path, resource_name))
249
+ resource_name = resource_name.replace('\\', '/').replace(os.path.sep, '/')
250
+ if sys.platform.startswith('win') and os.path.isabs(resource_name):
251
+ resource_name = '/' + resource_name
252
+ if is_dir and not resource_name.endswith('/'):
253
+ resource_name += '/'
254
+ return resource_name
255
+
256
+
257
+ ######################################################################
258
+ # Path Pointers
259
+ ######################################################################
260
+
261
+ @add_metaclass(ABCMeta)
262
+ class PathPointer(object):
263
+ """
264
+ An abstract base class for 'path pointers,' used by NLTK's data
265
+ package to identify specific paths. Two subclasses exist:
266
+ ``FileSystemPathPointer`` identifies a file that can be accessed
267
+ directly via a given absolute path. ``ZipFilePathPointer``
268
+ identifies a file contained within a zipfile, that can be accessed
269
+ by reading that zipfile.
270
+ """
271
+
272
+ @abstractmethod
273
+ def open(self, encoding=None):
274
+ """
275
+ Return a seekable read-only stream that can be used to read
276
+ the contents of the file identified by this path pointer.
277
+
278
+ :raise IOError: If the path specified by this pointer does
279
+ not contain a readable file.
280
+ """
281
+
282
+ @abstractmethod
283
+ def file_size(self):
284
+ """
285
+ Return the size of the file pointed to by this path pointer,
286
+ in bytes.
287
+
288
+ :raise IOError: If the path specified by this pointer does
289
+ not contain a readable file.
290
+ """
291
+
292
+ @abstractmethod
293
+ def join(self, fileid):
294
+ """
295
+ Return a new path pointer formed by starting at the path
296
+ identified by this pointer, and then following the relative
297
+ path given by ``fileid``. The path components of ``fileid``
298
+ should be separated by forward slashes, regardless of
299
+ the underlying file system's path seperator character.
300
+ """
301
+
302
+
303
+ class FileSystemPathPointer(PathPointer, text_type):
304
+ """
305
+ A path pointer that identifies a file which can be accessed
306
+ directly via a given absolute path.
307
+ """
308
+ @py3_data
309
+ def __init__(self, _path):
310
+ """
311
+ Create a new path pointer for the given absolute path.
312
+
313
+ :raise IOError: If the given path does not exist.
314
+ """
315
+
316
+ _path = os.path.abspath(_path)
317
+ if not os.path.exists(_path):
318
+ raise IOError('No such file or directory: %r' % _path)
319
+ self._path = _path
320
+
321
+ # There's no need to call str.__init__(), since it's a no-op;
322
+ # str does all of its setup work in __new__.
323
+
324
+ @property
325
+ def path(self):
326
+ """The absolute path identified by this path pointer."""
327
+ return self._path
328
+
329
+ def open(self, encoding=None):
330
+ stream = open(self._path, 'rb')
331
+ if encoding is not None:
332
+ stream = SeekableUnicodeStreamReader(stream, encoding)
333
+ return stream
334
+
335
+ def file_size(self):
336
+ return os.stat(self._path).st_size
337
+
338
+ def join(self, fileid):
339
+ _path = os.path.join(self._path, fileid)
340
+ return FileSystemPathPointer(_path)
341
+
342
+ def __repr__(self):
343
+ # This should be a byte string under Python 2.x;
344
+ # we don't want transliteration here so
345
+ # @python_2_unicode_compatible is not used.
346
+ return str('FileSystemPathPointer(%r)' % self._path)
347
+
348
+ def __str__(self):
349
+ return self._path
350
+
351
+
352
+ class BufferedGzipFile(GzipFile):
353
+ """
354
+ A ``GzipFile`` subclass that buffers calls to ``read()`` and ``write()``.
355
+ This allows faster reads and writes of data to and from gzip-compressed
356
+ files at the cost of using more memory.
357
+
358
+ The default buffer size is 2MB.
359
+
360
+ ``BufferedGzipFile`` is useful for loading large gzipped pickle objects
361
+ as well as writing large encoded feature files for classifier training.
362
+ """
363
+ MB = 2 ** 20
364
+ SIZE = 2 * MB
365
+
366
+ @py3_data
367
+ def __init__(self, filename=None, mode=None, compresslevel=9,
368
+ fileobj=None, **kwargs):
369
+ """
370
+ Return a buffered gzip file object.
371
+
372
+ :param filename: a filesystem path
373
+ :type filename: str
374
+ :param mode: a file mode which can be any of 'r', 'rb', 'a', 'ab',
375
+ 'w', or 'wb'
376
+ :type mode: str
377
+ :param compresslevel: The compresslevel argument is an integer from 1
378
+ to 9 controlling the level of compression; 1 is fastest and
379
+ produces the least compression, and 9 is slowest and produces the
380
+ most compression. The default is 9.
381
+ :type compresslevel: int
382
+ :param fileobj: a BytesIO stream to read from instead of a file.
383
+ :type fileobj: BytesIO
384
+ :param size: number of bytes to buffer during calls to read() and write()
385
+ :type size: int
386
+ :rtype: BufferedGzipFile
387
+ """
388
+ GzipFile.__init__(self, filename, mode, compresslevel, fileobj)
389
+ self._size = kwargs.get('size', self.SIZE)
390
+ self._nltk_buffer = BytesIO()
391
+ # cStringIO does not support len.
392
+ self._len = 0
393
+
394
+ def _reset_buffer(self):
395
+ # For some reason calling BytesIO.truncate() here will lead to
396
+ # inconsistent writes so just set _buffer to a new BytesIO object.
397
+ self._nltk_buffer = BytesIO()
398
+ self._len = 0
399
+
400
+ def _write_buffer(self, data):
401
+ # Simply write to the buffer and increment the buffer size.
402
+ if data is not None:
403
+ self._nltk_buffer.write(data)
404
+ self._len += len(data)
405
+
406
+ def _write_gzip(self, data):
407
+ # Write the current buffer to the GzipFile.
408
+ GzipFile.write(self, self._nltk_buffer.getvalue())
409
+ # Then reset the buffer and write the new data to the buffer.
410
+ self._reset_buffer()
411
+ self._write_buffer(data)
412
+
413
+ def close(self):
414
+ # GzipFile.close() doesn't actuallly close anything.
415
+ if self.mode == GZ_WRITE:
416
+ self._write_gzip(None)
417
+ self._reset_buffer()
418
+ return GzipFile.close(self)
419
+
420
+ def flush(self, lib_mode=FLUSH):
421
+ self._nltk_buffer.flush()
422
+ GzipFile.flush(self, lib_mode)
423
+
424
+ def read(self, size=None):
425
+ if not size:
426
+ size = self._size
427
+ contents = BytesIO()
428
+ while True:
429
+ blocks = GzipFile.read(self, size)
430
+ if not blocks:
431
+ contents.flush()
432
+ break
433
+ contents.write(blocks)
434
+ return contents.getvalue()
435
+ else:
436
+ return GzipFile.read(self, size)
437
+
438
+ def write(self, data, size=-1):
439
+ """
440
+ :param data: bytes to write to file or buffer
441
+ :type data: bytes
442
+ :param size: buffer at least size bytes before writing to file
443
+ :type size: int
444
+ """
445
+ if not size:
446
+ size = self._size
447
+ if self._len + len(data) <= size:
448
+ self._write_buffer(data)
449
+ else:
450
+ self._write_gzip(data)
451
+
452
+
453
+ class GzipFileSystemPathPointer(FileSystemPathPointer):
454
+ """
455
+ A subclass of ``FileSystemPathPointer`` that identifies a gzip-compressed
456
+ file located at a given absolute path. ``GzipFileSystemPathPointer`` is
457
+ appropriate for loading large gzip-compressed pickle objects efficiently.
458
+ """
459
+
460
+ def open(self, encoding=None):
461
+ # Note: In >= Python3.5, GzipFile is already using a
462
+ # buffered reader in the backend which has a variable self._buffer
463
+ # See https://github.com/nltk/nltk/issues/1308
464
+ if sys.version.startswith('2.7') or sys.version.startswith('3.4'):
465
+ stream = BufferedGzipFile(self._path, 'rb')
466
+ else:
467
+ stream = GzipFile(self._path, 'rb')
468
+ if encoding:
469
+ stream = SeekableUnicodeStreamReader(stream, encoding)
470
+ return stream
471
+
472
+
473
+ class ZipFilePathPointer(PathPointer):
474
+ """
475
+ A path pointer that identifies a file contained within a zipfile,
476
+ which can be accessed by reading that zipfile.
477
+ """
478
+ @py3_data
479
+ def __init__(self, zipfile, entry=''):
480
+ """
481
+ Create a new path pointer pointing at the specified entry
482
+ in the given zipfile.
483
+
484
+ :raise IOError: If the given zipfile does not exist, or if it
485
+ does not contain the specified entry.
486
+ """
487
+ if isinstance(zipfile, string_types):
488
+ zipfile = OpenOnDemandZipFile(os.path.abspath(zipfile))
489
+
490
+ # Normalize the entry string, it should be relative:
491
+ entry = normalize_resource_name(entry, True, '/').lstrip('/')
492
+
493
+ # Check that the entry exists:
494
+ if entry != '.':
495
+ try:
496
+ zipfile.getinfo(entry)
497
+ except Exception:
498
+ # Sometimes directories aren't explicitly listed in
499
+ # the zip file. So if `entry` is a directory name,
500
+ # then check if the zipfile contains any files that
501
+ # are under the given directory.
502
+ if (entry.endswith('/') and
503
+ [n for n in zipfile.namelist() if n.startswith(entry)]):
504
+ pass # zipfile contains a file in that directory.
505
+ else:
506
+ # Otherwise, complain.
507
+ raise IOError('Zipfile %r does not contain %r' %
508
+ (zipfile.filename, entry))
509
+ self._zipfile = zipfile
510
+ self._entry = entry
511
+
512
+ @property
513
+ def zipfile(self):
514
+ """
515
+ The zipfile.ZipFile object used to access the zip file
516
+ containing the entry identified by this path pointer.
517
+ """
518
+ return self._zipfile
519
+
520
+ @property
521
+ def entry(self):
522
+ """
523
+ The name of the file within zipfile that this path
524
+ pointer points to.
525
+ """
526
+ return self._entry
527
+
528
+ def open(self, encoding=None):
529
+ data = self._zipfile.read(self._entry)
530
+ stream = BytesIO(data)
531
+ if self._entry.endswith('.gz'):
532
+ # Note: In >= Python3.5, GzipFile is already using a
533
+ # buffered reader in the backend which has a variable self._buffer
534
+ # See https://github.com/nltk/nltk/issues/1308
535
+ if sys.version.startswith('2.7') or sys.version.startswith('3.4'):
536
+ stream = BufferedGzipFile(self._entry, fileobj=stream)
537
+ else:
538
+ stream = GzipFile(self._entry, fileobj=stream)
539
+ elif encoding is not None:
540
+ stream = SeekableUnicodeStreamReader(stream, encoding)
541
+ return stream
542
+
543
+ def file_size(self):
544
+ return self._zipfile.getinfo(self._entry).file_size
545
+
546
+ def join(self, fileid):
547
+ entry = '%s/%s' % (self._entry, fileid)
548
+ return ZipFilePathPointer(self._zipfile, entry)
549
+
550
+ def __repr__(self):
551
+ return str('ZipFilePathPointer(%r, %r)') % (
552
+ self._zipfile.filename, self._entry)
553
+
554
+ def __str__(self):
555
+ return os.path.normpath(os.path.join(self._zipfile.filename,
556
+ self._entry))
557
+
558
+
559
+ ######################################################################
560
+ # Access Functions
561
+ ######################################################################
562
+
563
+ # Don't use a weak dictionary, because in the common case this
564
+ # causes a lot more reloading that necessary.
565
+ _resource_cache = {}
566
+ """A dictionary used to cache resources so that they won't
567
+ need to be loaded more than once."""
568
+
569
+
570
+ def find(resource_name, paths=None):
571
+ """
572
+ Find the given resource by searching through the directories and
573
+ zip files in paths, where a None or empty string specifies an absolute path.
574
+ Returns a corresponding path name. If the given resource is not
575
+ found, raise a ``LookupError``, whose message gives a pointer to
576
+ the installation instructions for the NLTK downloader.
577
+
578
+ Zip File Handling:
579
+
580
+ - If ``resource_name`` contains a component with a ``.zip``
581
+ extension, then it is assumed to be a zipfile; and the
582
+ remaining path components are used to look inside the zipfile.
583
+
584
+ - If any element of ``nltk.data.path`` has a ``.zip`` extension,
585
+ then it is assumed to be a zipfile.
586
+
587
+ - If a given resource name that does not contain any zipfile
588
+ component is not found initially, then ``find()`` will make a
589
+ second attempt to find that resource, by replacing each
590
+ component *p* in the path with *p.zip/p*. For example, this
591
+ allows ``find()`` to map the resource name
592
+ ``corpora/chat80/cities.pl`` to a zip file path pointer to
593
+ ``corpora/chat80.zip/chat80/cities.pl``.
594
+
595
+ - When using ``find()`` to locate a directory contained in a
596
+ zipfile, the resource name must end with the forward slash
597
+ character. Otherwise, ``find()`` will not locate the
598
+ directory.
599
+
600
+ :type resource_name: str or unicode
601
+ :param resource_name: The name of the resource to search for.
602
+ Resource names are posix-style relative path names, such as
603
+ ``corpora/brown``. Directory names will be
604
+ automatically converted to a platform-appropriate path separator.
605
+ :rtype: str
606
+ """
607
+ resource_name = normalize_resource_name(resource_name, True)
608
+
609
+ # Resolve default paths at runtime in-case the user overrides
610
+ # nltk.data.path
611
+ if paths is None:
612
+ paths = path
613
+
614
+ # Check if the resource name includes a zipfile name
615
+ m = re.match(r'(.*\.zip)/?(.*)$|', resource_name)
616
+ zipfile, zipentry = m.groups()
617
+
618
+ # Check each item in our path
619
+ for path_ in paths:
620
+ # Is the path item a zipfile?
621
+ if path_ and (os.path.isfile(path_) and path_.endswith('.zip')):
622
+ try:
623
+ return ZipFilePathPointer(path_, resource_name)
624
+ except IOError:
625
+ # resource not in zipfile
626
+ continue
627
+
628
+ # Is the path item a directory or is resource_name an absolute path?
629
+ elif not path_ or os.path.isdir(path_):
630
+ if zipfile is None:
631
+ p = os.path.join(path_, url2pathname(resource_name))
632
+ if os.path.exists(p):
633
+ if p.endswith('.gz'):
634
+ return GzipFileSystemPathPointer(p)
635
+ else:
636
+ return FileSystemPathPointer(p)
637
+ else:
638
+ p = os.path.join(path_, url2pathname(zipfile))
639
+ if os.path.exists(p):
640
+ try:
641
+ return ZipFilePathPointer(p, zipentry)
642
+ except IOError:
643
+ # resource not in zipfile
644
+ continue
645
+
646
+ # Fallback: if the path doesn't include a zip file, then try
647
+ # again, assuming that one of the path components is inside a
648
+ # zipfile of the same name.
649
+ if zipfile is None:
650
+ pieces = resource_name.split('/')
651
+ for i in range(len(pieces)):
652
+ modified_name = '/'.join(pieces[:i] +
653
+ [pieces[i] + '.zip'] + pieces[i:])
654
+ try:
655
+ return find(modified_name, paths)
656
+ except LookupError:
657
+ pass
658
+
659
+ # Identify the package (i.e. the .zip file) to download.
660
+ resource_zipname = resource_name.split('/')[1]
661
+ if resource_zipname.endswith('.zip'):
662
+ resource_zipname = resource_zipname.rpartition('.')[0]
663
+ # Display a friendly error message if the resource wasn't found:
664
+ msg = str("Resource \33[93m{resource}\033[0m not found.\n"
665
+ "Please use the NLTK Downloader to obtain the resource:\n\n"
666
+ "\33[31m" # To display red text in terminal.
667
+ ">>> import nltk\n"
668
+ ">>> nltk.download(\'{resource}\')\n"
669
+ "\033[0m").format(resource=resource_zipname)
670
+ msg = textwrap_indent(msg)
671
+
672
+ msg += '\n Searched in:' + ''.join('\n - %r' % d for d in paths)
673
+ sep = '*' * 70
674
+ resource_not_found = '\n%s\n%s\n%s\n' % (sep, msg, sep)
675
+ raise LookupError(resource_not_found)
676
+
677
+
678
+ def retrieve(resource_url, filename=None, verbose=True):
679
+ """
680
+ Copy the given resource to a local file. If no filename is
681
+ specified, then use the URL's filename. If there is already a
682
+ file named ``filename``, then raise a ``ValueError``.
683
+
684
+ :type resource_url: str
685
+ :param resource_url: A URL specifying where the resource should be
686
+ loaded from. The default protocol is "nltk:", which searches
687
+ for the file in the the NLTK data package.
688
+ """
689
+ resource_url = normalize_resource_url(resource_url)
690
+ if filename is None:
691
+ if resource_url.startswith('file:'):
692
+ filename = os.path.split(resource_url)[-1]
693
+ else:
694
+ filename = re.sub(r'(^\w+:)?.*/', '', resource_url)
695
+ if os.path.exists(filename):
696
+ filename = os.path.abspath(filename)
697
+ raise ValueError("File %r already exists!" % filename)
698
+
699
+ if verbose:
700
+ print('Retrieving %r, saving to %r' % (resource_url, filename))
701
+
702
+ # Open the input & output streams.
703
+ infile = _open(resource_url)
704
+
705
+ # Copy infile -> outfile, using 64k blocks.
706
+ with open(filename, "wb") as outfile:
707
+ while True:
708
+ s = infile.read(1024 * 64) # 64k blocks.
709
+ outfile.write(s)
710
+ if not s:
711
+ break
712
+
713
+ infile.close()
714
+
715
+
716
+ #: A dictionary describing the formats that are supported by NLTK's
717
+ #: load() method. Keys are format names, and values are format
718
+ #: descriptions.
719
+ FORMATS = {
720
+ 'pickle': "A serialized python object, stored using the pickle module.",
721
+ 'json': "A serialized python object, stored using the json module.",
722
+ 'yaml': "A serialized python object, stored using the yaml module.",
723
+ 'cfg': "A context free grammar.",
724
+ 'pcfg': "A probabilistic CFG.",
725
+ 'fcfg': "A feature CFG.",
726
+ 'fol': "A list of first order logic expressions, parsed with "
727
+ "nltk.sem.logic.Expression.fromstring.",
728
+ 'logic': "A list of first order logic expressions, parsed with "
729
+ "nltk.sem.logic.LogicParser. Requires an additional logic_parser "
730
+ "parameter",
731
+ 'val': "A semantic valuation, parsed by nltk.sem.Valuation.fromstring.",
732
+ 'raw': "The raw (byte string) contents of a file.",
733
+ 'text': "The raw (unicode string) contents of a file. "
734
+ }
735
+
736
+ #: A dictionary mapping from file extensions to format names, used
737
+ #: by load() when format="auto" to decide the format for a
738
+ #: given resource url.
739
+ AUTO_FORMATS = {
740
+ 'pickle': 'pickle',
741
+ 'json': 'json',
742
+ 'yaml': 'yaml',
743
+ 'cfg': 'cfg',
744
+ 'pcfg': 'pcfg',
745
+ 'fcfg': 'fcfg',
746
+ 'fol': 'fol',
747
+ 'logic': 'logic',
748
+ 'val': 'val',
749
+ 'txt': 'text',
750
+ 'text': 'text',
751
+ }
752
+
753
+
754
+ def load(resource_url, format='auto', cache=True, verbose=False,
755
+ logic_parser=None, fstruct_reader=None, encoding=None):
756
+ """
757
+ Load a given resource from the NLTK data package. The following
758
+ resource formats are currently supported:
759
+
760
+ - ``pickle``
761
+ - ``json``
762
+ - ``yaml``
763
+ - ``cfg`` (context free grammars)
764
+ - ``pcfg`` (probabilistic CFGs)
765
+ - ``fcfg`` (feature-based CFGs)
766
+ - ``fol`` (formulas of First Order Logic)
767
+ - ``logic`` (Logical formulas to be parsed by the given logic_parser)
768
+ - ``val`` (valuation of First Order Logic model)
769
+ - ``text`` (the file contents as a unicode string)
770
+ - ``raw`` (the raw file contents as a byte string)
771
+
772
+ If no format is specified, ``load()`` will attempt to determine a
773
+ format based on the resource name's file extension. If that
774
+ fails, ``load()`` will raise a ``ValueError`` exception.
775
+
776
+ For all text formats (everything except ``pickle``, ``json``, ``yaml`` and ``raw``),
777
+ it tries to decode the raw contents using UTF-8, and if that doesn't
778
+ work, it tries with ISO-8859-1 (Latin-1), unless the ``encoding``
779
+ is specified.
780
+
781
+ :type resource_url: str
782
+ :param resource_url: A URL specifying where the resource should be
783
+ loaded from. The default protocol is "nltk:", which searches
784
+ for the file in the the NLTK data package.
785
+ :type cache: bool
786
+ :param cache: If true, add this resource to a cache. If load()
787
+ finds a resource in its cache, then it will return it from the
788
+ cache rather than loading it. The cache uses weak references,
789
+ so a resource wil automatically be expunged from the cache
790
+ when no more objects are using it.
791
+ :type verbose: bool
792
+ :param verbose: If true, print a message when loading a resource.
793
+ Messages are not displayed when a resource is retrieved from
794
+ the cache.
795
+ :type logic_parser: LogicParser
796
+ :param logic_parser: The parser that will be used to parse logical
797
+ expressions.
798
+ :type fstruct_reader: FeatStructReader
799
+ :param fstruct_reader: The parser that will be used to parse the
800
+ feature structure of an fcfg.
801
+ :type encoding: str
802
+ :param encoding: the encoding of the input; only used for text formats.
803
+ """
804
+ resource_url = normalize_resource_url(resource_url)
805
+ resource_url = add_py3_data(resource_url)
806
+
807
+ # Determine the format of the resource.
808
+ if format == 'auto':
809
+ resource_url_parts = resource_url.split('.')
810
+ ext = resource_url_parts[-1]
811
+ if ext == 'gz':
812
+ ext = resource_url_parts[-2]
813
+ format = AUTO_FORMATS.get(ext)
814
+ if format is None:
815
+ raise ValueError('Could not determine format for %s based '
816
+ 'on its file\nextension; use the "format" '
817
+ 'argument to specify the format explicitly.'
818
+ % resource_url)
819
+
820
+ if format not in FORMATS:
821
+ raise ValueError('Unknown format type: %s!' % (format,))
822
+
823
+ # If we've cached the resource, then just return it.
824
+ if cache:
825
+ resource_val = _resource_cache.get((resource_url, format))
826
+ if resource_val is not None:
827
+ if verbose:
828
+ print('<<Using cached copy of %s>>' % (resource_url,))
829
+ return resource_val
830
+
831
+ # Let the user know what's going on.
832
+ if verbose:
833
+ print('<<Loading %s>>' % (resource_url,))
834
+
835
+ # Load the resource.
836
+ opened_resource = _open(resource_url)
837
+
838
+ if format == 'raw':
839
+ resource_val = opened_resource.read()
840
+ elif format == 'pickle':
841
+ resource_val = pickle.load(opened_resource)
842
+ elif format == 'json':
843
+ import json
844
+ from nltk.jsontags import json_tags
845
+ resource_val = json.load(opened_resource)
846
+ tag = None
847
+ if len(resource_val) != 1:
848
+ tag = next(resource_val.keys())
849
+ if tag not in json_tags:
850
+ raise ValueError('Unknown json tag.')
851
+ elif format == 'yaml':
852
+ import yaml
853
+ resource_val = yaml.load(opened_resource)
854
+ else:
855
+ # The resource is a text format.
856
+ binary_data = opened_resource.read()
857
+ if encoding is not None:
858
+ string_data = binary_data.decode(encoding)
859
+ else:
860
+ try:
861
+ string_data = binary_data.decode('utf-8')
862
+ except UnicodeDecodeError:
863
+ string_data = binary_data.decode('latin-1')
864
+ if format == 'text':
865
+ resource_val = string_data
866
+ elif format == 'cfg':
867
+ resource_val = nltk.grammar.CFG.fromstring(
868
+ string_data, encoding=encoding)
869
+ elif format == 'pcfg':
870
+ resource_val = nltk.grammar.PCFG.fromstring(
871
+ string_data, encoding=encoding)
872
+ elif format == 'fcfg':
873
+ resource_val = nltk.grammar.FeatureGrammar.fromstring(
874
+ string_data, logic_parser=logic_parser,
875
+ fstruct_reader=fstruct_reader, encoding=encoding)
876
+ elif format == 'fol':
877
+ resource_val = nltk.sem.read_logic(
878
+ string_data, logic_parser=nltk.sem.logic.LogicParser(),
879
+ encoding=encoding)
880
+ elif format == 'logic':
881
+ resource_val = nltk.sem.read_logic(
882
+ string_data, logic_parser=logic_parser, encoding=encoding)
883
+ elif format == 'val':
884
+ resource_val = nltk.sem.read_valuation(
885
+ string_data, encoding=encoding)
886
+ else:
887
+ raise AssertionError("Internal NLTK error: Format %s isn't "
888
+ "handled by nltk.data.load()" % (format,))
889
+
890
+ opened_resource.close()
891
+
892
+ # If requested, add it to the cache.
893
+ if cache:
894
+ try:
895
+ _resource_cache[(resource_url, format)] = resource_val
896
+ # TODO: add this line
897
+ # print('<<Caching a copy of %s>>' % (resource_url,))
898
+ except TypeError:
899
+ # We can't create weak references to some object types, like
900
+ # strings and tuples. For now, just don't cache them.
901
+ pass
902
+
903
+ return resource_val
904
+
905
+
906
+ def show_cfg(resource_url, escape='##'):
907
+ """
908
+ Write out a grammar file, ignoring escaped and empty lines.
909
+
910
+ :type resource_url: str
911
+ :param resource_url: A URL specifying where the resource should be
912
+ loaded from. The default protocol is "nltk:", which searches
913
+ for the file in the the NLTK data package.
914
+ :type escape: str
915
+ :param escape: Prepended string that signals lines to be ignored
916
+ """
917
+ resource_url = normalize_resource_url(resource_url)
918
+ resource_val = load(resource_url, format='text', cache=False)
919
+ lines = resource_val.splitlines()
920
+ for l in lines:
921
+ if l.startswith(escape):
922
+ continue
923
+ if re.match('^$', l):
924
+ continue
925
+ print(l)
926
+
927
+
928
+ def clear_cache():
929
+ """
930
+ Remove all objects from the resource cache.
931
+ :see: load()
932
+ """
933
+ _resource_cache.clear()
934
+
935
+
936
+ def _open(resource_url):
937
+ """
938
+ Helper function that returns an open file object for a resource,
939
+ given its resource URL. If the given resource URL uses the "nltk:"
940
+ protocol, or uses no protocol, then use ``nltk.data.find`` to find
941
+ its path, and open it with the given mode; if the resource URL
942
+ uses the 'file' protocol, then open the file with the given mode;
943
+ otherwise, delegate to ``urllib2.urlopen``.
944
+
945
+ :type resource_url: str
946
+ :param resource_url: A URL specifying where the resource should be
947
+ loaded from. The default protocol is "nltk:", which searches
948
+ for the file in the the NLTK data package.
949
+ """
950
+ resource_url = normalize_resource_url(resource_url)
951
+ protocol, path_ = split_resource_url(resource_url)
952
+
953
+ if protocol is None or protocol.lower() == 'nltk':
954
+ return find(path_, path + ['']).open()
955
+ elif protocol.lower() == 'file':
956
+ # urllib might not use mode='rb', so handle this one ourselves:
957
+ return find(path_, ['']).open()
958
+ else:
959
+ return urlopen(resource_url)
960
+
961
+ ######################################################################
962
+ # Lazy Resource Loader
963
+ ######################################################################
964
+
965
+ # We shouldn't apply @python_2_unicode_compatible
966
+ # decorator to LazyLoader, this is resource.__class__ responsibility.
967
+
968
+
969
+ class LazyLoader(object):
970
+
971
+ @py3_data
972
+ def __init__(self, _path):
973
+ self._path = _path
974
+
975
+ def __load(self):
976
+ resource = load(self._path)
977
+ # This is where the magic happens! Transform ourselves into
978
+ # the object by modifying our own __dict__ and __class__ to
979
+ # match that of `resource`.
980
+ self.__dict__ = resource.__dict__
981
+ self.__class__ = resource.__class__
982
+
983
+ def __getattr__(self, attr):
984
+ self.__load()
985
+ # This looks circular, but its not, since __load() changes our
986
+ # __class__ to something new:
987
+ return getattr(self, attr)
988
+
989
+ def __repr__(self):
990
+ self.__load()
991
+ # This looks circular, but its not, since __load() changes our
992
+ # __class__ to something new:
993
+ return repr(self)
994
+
995
+ ######################################################################
996
+ # Open-On-Demand ZipFile
997
+ ######################################################################
998
+
999
+
1000
+ class OpenOnDemandZipFile(zipfile.ZipFile):
1001
+ """
1002
+ A subclass of ``zipfile.ZipFile`` that closes its file pointer
1003
+ whenever it is not using it; and re-opens it when it needs to read
1004
+ data from the zipfile. This is useful for reducing the number of
1005
+ open file handles when many zip files are being accessed at once.
1006
+ ``OpenOnDemandZipFile`` must be constructed from a filename, not a
1007
+ file-like object (to allow re-opening). ``OpenOnDemandZipFile`` is
1008
+ read-only (i.e. ``write()`` and ``writestr()`` are disabled.
1009
+ """
1010
+ @py3_data
1011
+ def __init__(self, filename):
1012
+ if not isinstance(filename, string_types):
1013
+ raise TypeError('ReopenableZipFile filename must be a string')
1014
+ zipfile.ZipFile.__init__(self, filename)
1015
+ assert self.filename == filename
1016
+ self.close()
1017
+ # After closing a ZipFile object, the _fileRefCnt needs to be cleared
1018
+ # for Python2and3 compatible code.
1019
+ self._fileRefCnt = 0
1020
+
1021
+ def read(self, name):
1022
+ assert self.fp is None
1023
+ self.fp = open(self.filename, 'rb')
1024
+ value = zipfile.ZipFile.read(self, name)
1025
+ # Ensure that _fileRefCnt needs to be set for Python2and3 compatible code.
1026
+ # Since we only opened one file here, we add 1.
1027
+ self._fileRefCnt += 1
1028
+ self.close()
1029
+ return value
1030
+
1031
+ def write(self, *args, **kwargs):
1032
+ """:raise NotImplementedError: OpenOnDemandZipfile is read-only"""
1033
+ raise NotImplementedError('OpenOnDemandZipfile is read-only')
1034
+
1035
+ def writestr(self, *args, **kwargs):
1036
+ """:raise NotImplementedError: OpenOnDemandZipfile is read-only"""
1037
+ raise NotImplementedError('OpenOnDemandZipfile is read-only')
1038
+
1039
+ def __repr__(self):
1040
+ return repr(str('OpenOnDemandZipFile(%r)') % self.filename)
1041
+
1042
+ ######################################################################
1043
+ #{ Seekable Unicode Stream Reader
1044
+ ######################################################################
1045
+
1046
+
1047
+ class SeekableUnicodeStreamReader(object):
1048
+ """
1049
+ A stream reader that automatically encodes the source byte stream
1050
+ into unicode (like ``codecs.StreamReader``); but still supports the
1051
+ ``seek()`` and ``tell()`` operations correctly. This is in contrast
1052
+ to ``codecs.StreamReader``, which provide *broken* ``seek()`` and
1053
+ ``tell()`` methods.
1054
+
1055
+ This class was motivated by ``StreamBackedCorpusView``, which
1056
+ makes extensive use of ``seek()`` and ``tell()``, and needs to be
1057
+ able to handle unicode-encoded files.
1058
+
1059
+ Note: this class requires stateless decoders. To my knowledge,
1060
+ this shouldn't cause a problem with any of python's builtin
1061
+ unicode encodings.
1062
+ """
1063
+ DEBUG = True # : If true, then perform extra sanity checks.
1064
+
1065
+ @py3_data
1066
+ def __init__(self, stream, encoding, errors='strict'):
1067
+ # Rewind the stream to its beginning.
1068
+ stream.seek(0)
1069
+
1070
+ self.stream = stream
1071
+ """The underlying stream."""
1072
+
1073
+ self.encoding = encoding
1074
+ """The name of the encoding that should be used to encode the
1075
+ underlying stream."""
1076
+
1077
+ self.errors = errors
1078
+ """The error mode that should be used when decoding data from
1079
+ the underlying stream. Can be 'strict', 'ignore', or
1080
+ 'replace'."""
1081
+
1082
+ self.decode = codecs.getdecoder(encoding)
1083
+ """The function that is used to decode byte strings into
1084
+ unicode strings."""
1085
+
1086
+ self.bytebuffer = b''
1087
+ """A buffer to use bytes that have been read but have not yet
1088
+ been decoded. This is only used when the final bytes from
1089
+ a read do not form a complete encoding for a character."""
1090
+
1091
+ self.linebuffer = None
1092
+ """A buffer used by ``readline()`` to hold characters that have
1093
+ been read, but have not yet been returned by ``read()`` or
1094
+ ``readline()``. This buffer consists of a list of unicode
1095
+ strings, where each string corresponds to a single line.
1096
+ The final element of the list may or may not be a complete
1097
+ line. Note that the existence of a linebuffer makes the
1098
+ ``tell()`` operation more complex, because it must backtrack
1099
+ to the beginning of the buffer to determine the correct
1100
+ file position in the underlying byte stream."""
1101
+
1102
+ self._rewind_checkpoint = 0
1103
+ """The file position at which the most recent read on the
1104
+ underlying stream began. This is used, together with
1105
+ ``_rewind_numchars``, to backtrack to the beginning of
1106
+ ``linebuffer`` (which is required by ``tell()``)."""
1107
+
1108
+ self._rewind_numchars = None
1109
+ """The number of characters that have been returned since the
1110
+ read that started at ``_rewind_checkpoint``. This is used,
1111
+ together with ``_rewind_checkpoint``, to backtrack to the
1112
+ beginning of ``linebuffer`` (which is required by ``tell()``)."""
1113
+
1114
+ self._bom = self._check_bom()
1115
+ """The length of the byte order marker at the beginning of
1116
+ the stream (or None for no byte order marker)."""
1117
+
1118
+ #/////////////////////////////////////////////////////////////////
1119
+ # Read methods
1120
+ #/////////////////////////////////////////////////////////////////
1121
+
1122
+ def read(self, size=None):
1123
+ """
1124
+ Read up to ``size`` bytes, decode them using this reader's
1125
+ encoding, and return the resulting unicode string.
1126
+
1127
+ :param size: The maximum number of bytes to read. If not
1128
+ specified, then read as many bytes as possible.
1129
+ :type size: int
1130
+ :rtype: unicode
1131
+ """
1132
+ chars = self._read(size)
1133
+
1134
+ # If linebuffer is not empty, then include it in the result
1135
+ if self.linebuffer:
1136
+ chars = ''.join(self.linebuffer) + chars
1137
+ self.linebuffer = None
1138
+ self._rewind_numchars = None
1139
+
1140
+ return chars
1141
+
1142
+ def readline(self, size=None):
1143
+ """
1144
+ Read a line of text, decode it using this reader's encoding,
1145
+ and return the resulting unicode string.
1146
+
1147
+ :param size: The maximum number of bytes to read. If no
1148
+ newline is encountered before ``size`` bytes have been read,
1149
+ then the returned value may not be a complete line of text.
1150
+ :type size: int
1151
+ """
1152
+ # If we have a non-empty linebuffer, then return the first
1153
+ # line from it. (Note that the last element of linebuffer may
1154
+ # not be a complete line; so let _read() deal with it.)
1155
+ if self.linebuffer and len(self.linebuffer) > 1:
1156
+ line = self.linebuffer.pop(0)
1157
+ self._rewind_numchars += len(line)
1158
+ return line
1159
+
1160
+ readsize = size or 72
1161
+ chars = ''
1162
+
1163
+ # If there's a remaining incomplete line in the buffer, add it.
1164
+ if self.linebuffer:
1165
+ chars += self.linebuffer.pop()
1166
+ self.linebuffer = None
1167
+
1168
+ while True:
1169
+ startpos = self.stream.tell() - len(self.bytebuffer)
1170
+ new_chars = self._read(readsize)
1171
+
1172
+ # If we're at a '\r', then read one extra character, since
1173
+ # it might be a '\n', to get the proper line ending.
1174
+ if new_chars and new_chars.endswith('\r'):
1175
+ new_chars += self._read(1)
1176
+
1177
+ chars += new_chars
1178
+ lines = chars.splitlines(True)
1179
+ if len(lines) > 1:
1180
+ line = lines[0]
1181
+ self.linebuffer = lines[1:]
1182
+ self._rewind_numchars = (len(new_chars) -
1183
+ (len(chars) - len(line)))
1184
+ self._rewind_checkpoint = startpos
1185
+ break
1186
+ elif len(lines) == 1:
1187
+ line0withend = lines[0]
1188
+ line0withoutend = lines[0].splitlines(False)[0]
1189
+ if line0withend != line0withoutend: # complete line
1190
+ line = line0withend
1191
+ break
1192
+
1193
+ if not new_chars or size is not None:
1194
+ line = chars
1195
+ break
1196
+
1197
+ # Read successively larger blocks of text.
1198
+ if readsize < 8000:
1199
+ readsize *= 2
1200
+
1201
+ return line
1202
+
1203
+ def readlines(self, sizehint=None, keepends=True):
1204
+ """
1205
+ Read this file's contents, decode them using this reader's
1206
+ encoding, and return it as a list of unicode lines.
1207
+
1208
+ :rtype: list(unicode)
1209
+ :param sizehint: Ignored.
1210
+ :param keepends: If false, then strip newlines.
1211
+ """
1212
+ return self.read().splitlines(keepends)
1213
+
1214
+ def next(self):
1215
+ """Return the next decoded line from the underlying stream."""
1216
+ line = self.readline()
1217
+ if line:
1218
+ return line
1219
+ else:
1220
+ raise StopIteration
1221
+
1222
+ def __next__(self):
1223
+ return self.next()
1224
+
1225
+ def __iter__(self):
1226
+ """Return self"""
1227
+ return self
1228
+
1229
+ def xreadlines(self):
1230
+ """Return self"""
1231
+ return self
1232
+
1233
+ #/////////////////////////////////////////////////////////////////
1234
+ # Pass-through methods & properties
1235
+ #/////////////////////////////////////////////////////////////////
1236
+
1237
+ @property
1238
+ def closed(self):
1239
+ """True if the underlying stream is closed."""
1240
+ return self.stream.closed
1241
+
1242
+ @property
1243
+ def name(self):
1244
+ """The name of the underlying stream."""
1245
+ return self.stream.name
1246
+
1247
+ @property
1248
+ def mode(self):
1249
+ """The mode of the underlying stream."""
1250
+ return self.stream.mode
1251
+
1252
+ def close(self):
1253
+ """
1254
+ Close the underlying stream.
1255
+ """
1256
+ self.stream.close()
1257
+
1258
+ #/////////////////////////////////////////////////////////////////
1259
+ # Seek and tell
1260
+ #/////////////////////////////////////////////////////////////////
1261
+
1262
+ def seek(self, offset, whence=0):
1263
+ """
1264
+ Move the stream to a new file position. If the reader is
1265
+ maintaining any buffers, then they will be cleared.
1266
+
1267
+ :param offset: A byte count offset.
1268
+ :param whence: If 0, then the offset is from the start of the file
1269
+ (offset should be positive), if 1, then the offset is from the
1270
+ current position (offset may be positive or negative); and if 2,
1271
+ then the offset is from the end of the file (offset should
1272
+ typically be negative).
1273
+ """
1274
+ if whence == 1:
1275
+ raise ValueError('Relative seek is not supported for '
1276
+ 'SeekableUnicodeStreamReader -- consider '
1277
+ 'using char_seek_forward() instead.')
1278
+ self.stream.seek(offset, whence)
1279
+ self.linebuffer = None
1280
+ self.bytebuffer = b''
1281
+ self._rewind_numchars = None
1282
+ self._rewind_checkpoint = self.stream.tell()
1283
+
1284
+ def char_seek_forward(self, offset):
1285
+ """
1286
+ Move the read pointer forward by ``offset`` characters.
1287
+ """
1288
+ if offset < 0:
1289
+ raise ValueError('Negative offsets are not supported')
1290
+ # Clear all buffers.
1291
+ self.seek(self.tell())
1292
+ # Perform the seek operation.
1293
+ self._char_seek_forward(offset)
1294
+
1295
+ def _char_seek_forward(self, offset, est_bytes=None):
1296
+ """
1297
+ Move the file position forward by ``offset`` characters,
1298
+ ignoring all buffers.
1299
+
1300
+ :param est_bytes: A hint, giving an estimate of the number of
1301
+ bytes that will be needed to move forward by ``offset`` chars.
1302
+ Defaults to ``offset``.
1303
+ """
1304
+ if est_bytes is None:
1305
+ est_bytes = offset
1306
+ bytes = b''
1307
+
1308
+ while True:
1309
+ # Read in a block of bytes.
1310
+ newbytes = self.stream.read(est_bytes - len(bytes))
1311
+ bytes += newbytes
1312
+
1313
+ # Decode the bytes to characters.
1314
+ chars, bytes_decoded = self._incr_decode(bytes)
1315
+
1316
+ # If we got the right number of characters, then seek
1317
+ # backwards over any truncated characters, and return.
1318
+ if len(chars) == offset:
1319
+ self.stream.seek(-len(bytes) + bytes_decoded, 1)
1320
+ return
1321
+
1322
+ # If we went too far, then we can back-up until we get it
1323
+ # right, using the bytes we've already read.
1324
+ if len(chars) > offset:
1325
+ while len(chars) > offset:
1326
+ # Assume at least one byte/char.
1327
+ est_bytes += offset - len(chars)
1328
+ chars, bytes_decoded = self._incr_decode(bytes[:est_bytes])
1329
+ self.stream.seek(-len(bytes) + bytes_decoded, 1)
1330
+ return
1331
+
1332
+ # Otherwise, we haven't read enough bytes yet; loop again.
1333
+ est_bytes += offset - len(chars)
1334
+
1335
+ def tell(self):
1336
+ """
1337
+ Return the current file position on the underlying byte
1338
+ stream. If this reader is maintaining any buffers, then the
1339
+ returned file position will be the position of the beginning
1340
+ of those buffers.
1341
+ """
1342
+ # If nothing's buffered, then just return our current filepos:
1343
+ if self.linebuffer is None:
1344
+ return self.stream.tell() - len(self.bytebuffer)
1345
+
1346
+ # Otherwise, we'll need to backtrack the filepos until we
1347
+ # reach the beginning of the buffer.
1348
+
1349
+ # Store our original file position, so we can return here.
1350
+ orig_filepos = self.stream.tell()
1351
+
1352
+ # Calculate an estimate of where we think the newline is.
1353
+ bytes_read = ((orig_filepos - len(self.bytebuffer)) -
1354
+ self._rewind_checkpoint)
1355
+ buf_size = sum(len(line) for line in self.linebuffer)
1356
+ est_bytes = int((bytes_read * self._rewind_numchars /
1357
+ (self._rewind_numchars + buf_size)))
1358
+
1359
+ self.stream.seek(self._rewind_checkpoint)
1360
+ self._char_seek_forward(self._rewind_numchars, est_bytes)
1361
+ filepos = self.stream.tell()
1362
+
1363
+ # Sanity check
1364
+ if self.DEBUG:
1365
+ self.stream.seek(filepos)
1366
+ check1 = self._incr_decode(self.stream.read(50))[0]
1367
+ check2 = ''.join(self.linebuffer)
1368
+ assert check1.startswith(check2) or check2.startswith(check1)
1369
+
1370
+ # Return to our original filepos (so we don't have to throw
1371
+ # out our buffer.)
1372
+ self.stream.seek(orig_filepos)
1373
+
1374
+ # Return the calculated filepos
1375
+ return filepos
1376
+
1377
+ #/////////////////////////////////////////////////////////////////
1378
+ # Helper methods
1379
+ #/////////////////////////////////////////////////////////////////
1380
+
1381
+ def _read(self, size=None):
1382
+ """
1383
+ Read up to ``size`` bytes from the underlying stream, decode
1384
+ them using this reader's encoding, and return the resulting
1385
+ unicode string. ``linebuffer`` is not included in the result.
1386
+ """
1387
+ if size == 0:
1388
+ return ''
1389
+
1390
+ # Skip past the byte order marker, if present.
1391
+ if self._bom and self.stream.tell() == 0:
1392
+ self.stream.read(self._bom)
1393
+
1394
+ # Read the requested number of bytes.
1395
+ if size is None:
1396
+ new_bytes = self.stream.read()
1397
+ else:
1398
+ new_bytes = self.stream.read(size)
1399
+ bytes = self.bytebuffer + new_bytes
1400
+
1401
+ # Decode the bytes into unicode characters
1402
+ chars, bytes_decoded = self._incr_decode(bytes)
1403
+
1404
+ # If we got bytes but couldn't decode any, then read further.
1405
+ if (size is not None) and (not chars) and (len(new_bytes) > 0):
1406
+ while not chars:
1407
+ new_bytes = self.stream.read(1)
1408
+ if not new_bytes:
1409
+ break # end of file.
1410
+ bytes += new_bytes
1411
+ chars, bytes_decoded = self._incr_decode(bytes)
1412
+
1413
+ # Record any bytes we didn't consume.
1414
+ self.bytebuffer = bytes[bytes_decoded:]
1415
+
1416
+ # Return the result
1417
+ return chars
1418
+
1419
+ def _incr_decode(self, bytes):
1420
+ """
1421
+ Decode the given byte string into a unicode string, using this
1422
+ reader's encoding. If an exception is encountered that
1423
+ appears to be caused by a truncation error, then just decode
1424
+ the byte string without the bytes that cause the trunctaion
1425
+ error.
1426
+
1427
+ Return a tuple ``(chars, num_consumed)``, where ``chars`` is
1428
+ the decoded unicode string, and ``num_consumed`` is the
1429
+ number of bytes that were consumed.
1430
+ """
1431
+ while True:
1432
+ try:
1433
+ return self.decode(bytes, 'strict')
1434
+ except UnicodeDecodeError as exc:
1435
+ # If the exception occurs at the end of the string,
1436
+ # then assume that it's a truncation error.
1437
+ if exc.end == len(bytes):
1438
+ return self.decode(bytes[:exc.start], self.errors)
1439
+
1440
+ # Otherwise, if we're being strict, then raise it.
1441
+ elif self.errors == 'strict':
1442
+ raise
1443
+
1444
+ # If we're not strict, then re-process it with our
1445
+ # errors setting. This *may* raise an exception.
1446
+ else:
1447
+ return self.decode(bytes, self.errors)
1448
+
1449
+ _BOM_TABLE = {
1450
+ 'utf8': [(codecs.BOM_UTF8, None)],
1451
+ 'utf16': [(codecs.BOM_UTF16_LE, 'utf16-le'),
1452
+ (codecs.BOM_UTF16_BE, 'utf16-be')],
1453
+ 'utf16le': [(codecs.BOM_UTF16_LE, None)],
1454
+ 'utf16be': [(codecs.BOM_UTF16_BE, None)],
1455
+ 'utf32': [(codecs.BOM_UTF32_LE, 'utf32-le'),
1456
+ (codecs.BOM_UTF32_BE, 'utf32-be')],
1457
+ 'utf32le': [(codecs.BOM_UTF32_LE, None)],
1458
+ 'utf32be': [(codecs.BOM_UTF32_BE, None)],
1459
+ }
1460
+
1461
+ def _check_bom(self):
1462
+ # Normalize our encoding name
1463
+ enc = re.sub('[ -]', '', self.encoding.lower())
1464
+
1465
+ # Look up our encoding in the BOM table.
1466
+ bom_info = self._BOM_TABLE.get(enc)
1467
+
1468
+ if bom_info:
1469
+ # Read a prefix, to check against the BOM(s)
1470
+ bytes = self.stream.read(16)
1471
+ self.stream.seek(0)
1472
+
1473
+ # Check for each possible BOM.
1474
+ for (bom, new_encoding) in bom_info:
1475
+ if bytes.startswith(bom):
1476
+ if new_encoding:
1477
+ self.encoding = new_encoding
1478
+ return len(bom)
1479
+
1480
+ return None
1481
+
1482
+
1483
+ __all__ = ['path', 'PathPointer', 'FileSystemPathPointer', 'BufferedGzipFile',
1484
+ 'GzipFileSystemPathPointer', 'GzipFileSystemPathPointer',
1485
+ 'find', 'retrieve', 'FORMATS', 'AUTO_FORMATS', 'load',
1486
+ 'show_cfg', 'clear_cache', 'LazyLoader', 'OpenOnDemandZipFile',
1487
+ 'GzipFileSystemPathPointer', 'SeekableUnicodeStreamReader']