2015-12-07 05:01:28 +00:00
|
|
|
import os
|
2015-09-30 18:10:15 +00:00
|
|
|
import io
|
2015-01-05 06:54:13 +00:00
|
|
|
import json
|
2014-09-25 16:26:22 +00:00
|
|
|
import re
|
2015-12-29 15:55:03 +00:00
|
|
|
import os.path
|
2015-12-07 05:01:28 +00:00
|
|
|
|
2016-01-16 09:00:57 +00:00
|
|
|
import six
|
2016-01-13 18:46:17 +00:00
|
|
|
import sputnik
|
|
|
|
from sputnik.dir_package import DirPackage
|
2016-01-15 17:01:02 +00:00
|
|
|
from sputnik.package_list import (PackageNotFoundException,
|
|
|
|
CompatiblePackageNotFoundException)
|
2015-12-29 15:55:03 +00:00
|
|
|
|
2016-01-13 18:46:17 +00:00
|
|
|
from . import about
|
|
|
|
from .attrs import TAG, HEAD, DEP, ENT_IOB, ENT_TYPE
|
2015-12-29 15:55:03 +00:00
|
|
|
|
|
|
|
|
2016-03-25 17:54:45 +00:00
|
|
|
LANGUAGES = {}
|
|
|
|
|
|
|
|
|
|
|
|
def register_lang(name, cls):
|
|
|
|
global LANGUAGES
|
|
|
|
LANGUAGES[name] = cls
|
|
|
|
|
|
|
|
|
|
|
|
def get_lang(name):
|
|
|
|
lang = re.split('[^a-zA-Z0-9_]', name, 1)[0]
|
|
|
|
if lang not in LANGUAGES:
|
|
|
|
raise RuntimeError('Language not supported: %s' % lang)
|
|
|
|
return LANGUAGES[lang]
|
|
|
|
|
|
|
|
|
2016-01-16 11:23:45 +00:00
|
|
|
def get_package(data_dir):
|
|
|
|
if not isinstance(data_dir, six.string_types):
|
|
|
|
raise RuntimeError('data_dir must be a string')
|
|
|
|
return DirPackage(data_dir)
|
2015-12-31 03:13:15 +00:00
|
|
|
|
|
|
|
|
2016-01-15 17:57:01 +00:00
|
|
|
def get_package_by_name(name=None, via=None):
|
2016-03-25 17:54:45 +00:00
|
|
|
package_name = name or about.__models__[about.__default_lang__]
|
|
|
|
lang = get_lang(package_name)
|
2016-01-14 11:02:56 +00:00
|
|
|
try:
|
2016-02-15 00:33:39 +00:00
|
|
|
return sputnik.package(about.__title__, about.__version__,
|
2016-03-25 17:54:45 +00:00
|
|
|
package_name, data_path=via)
|
2016-01-14 11:02:56 +00:00
|
|
|
except PackageNotFoundException as e:
|
2016-03-25 17:54:45 +00:00
|
|
|
raise RuntimeError("Model '%s' not installed. Please run 'python -m "
|
|
|
|
"%s.download' to install latest compatible "
|
|
|
|
"model." % (name, lang.__module__))
|
2016-01-14 11:02:56 +00:00
|
|
|
except CompatiblePackageNotFoundException as e:
|
2016-03-25 17:54:45 +00:00
|
|
|
raise RuntimeError("Installed model is not compatible with spaCy "
|
|
|
|
"version. Please run 'python -m %s.download "
|
2016-03-24 10:19:43 +00:00
|
|
|
"--force' to install latest compatible model." %
|
2016-03-25 17:54:45 +00:00
|
|
|
(lang.__module__))
|
2014-09-25 16:26:22 +00:00
|
|
|
|
|
|
|
|
2015-10-07 08:25:35 +00:00
|
|
|
def normalize_slice(length, start, stop, step=None):
|
|
|
|
if not (step is None or step == 1):
|
|
|
|
raise ValueError("Stepped slices not supported in Span objects."
|
|
|
|
"Try: list(tokens)[start:stop:step] instead.")
|
|
|
|
if start is None:
|
|
|
|
start = 0
|
|
|
|
elif start < 0:
|
|
|
|
start += length
|
|
|
|
start = min(length, max(0, start))
|
|
|
|
|
|
|
|
if stop is None:
|
|
|
|
stop = length
|
|
|
|
elif stop < 0:
|
|
|
|
stop += length
|
|
|
|
stop = min(length, max(start, stop))
|
|
|
|
|
|
|
|
assert 0 <= start <= stop <= length
|
|
|
|
return start, stop
|
|
|
|
|
|
|
|
|
2014-09-25 16:26:22 +00:00
|
|
|
def utf8open(loc, mode='r'):
|
2015-09-30 18:10:15 +00:00
|
|
|
return io.open(loc, mode, encoding='utf8')
|
2014-09-25 16:26:22 +00:00
|
|
|
|
|
|
|
|
2015-12-07 05:01:28 +00:00
|
|
|
def read_lang_data(package):
|
2015-12-31 03:13:15 +00:00
|
|
|
tokenization = package.load_json(('tokenizer', 'specials.json'))
|
|
|
|
with package.open(('tokenizer', 'prefix.txt'), default=None) as file_:
|
|
|
|
prefix = read_prefix(file_) if file_ is not None else None
|
|
|
|
with package.open(('tokenizer', 'suffix.txt'), default=None) as file_:
|
|
|
|
suffix = read_suffix(file_) if file_ is not None else None
|
|
|
|
with package.open(('tokenizer', 'infix.txt'), default=None) as file_:
|
|
|
|
infix = read_infix(file_) if file_ is not None else None
|
2014-10-30 07:14:42 +00:00
|
|
|
return tokenization, prefix, suffix, infix
|
2014-09-25 16:26:22 +00:00
|
|
|
|
|
|
|
|
2015-12-07 05:01:28 +00:00
|
|
|
def read_prefix(fileobj):
|
|
|
|
entries = fileobj.read().split('\n')
|
|
|
|
expression = '|'.join(['^' + re.escape(piece) for piece in entries if piece.strip()])
|
2014-09-25 16:26:22 +00:00
|
|
|
return expression
|
|
|
|
|
2014-12-09 03:48:01 +00:00
|
|
|
|
2015-12-07 05:01:28 +00:00
|
|
|
def read_suffix(fileobj):
|
|
|
|
entries = fileobj.read().split('\n')
|
|
|
|
expression = '|'.join([piece + '$' for piece in entries if piece.strip()])
|
2014-09-25 16:26:22 +00:00
|
|
|
return expression
|
|
|
|
|
2014-12-09 03:48:01 +00:00
|
|
|
|
2015-12-07 05:01:28 +00:00
|
|
|
def read_infix(fileobj):
|
|
|
|
entries = fileobj.read().split('\n')
|
|
|
|
expression = '|'.join([piece for piece in entries if piece.strip()])
|
2014-10-14 04:47:06 +00:00
|
|
|
return expression
|
|
|
|
|
|
|
|
|
2015-12-07 05:01:28 +00:00
|
|
|
# def read_tokenization(lang):
|
|
|
|
# loc = path.join(DATA_DIR, lang, 'tokenization')
|
|
|
|
# entries = []
|
|
|
|
# seen = set()
|
|
|
|
# with utf8open(loc) as file_:
|
|
|
|
# for line in file_:
|
|
|
|
# line = line.strip()
|
|
|
|
# if line.startswith('#'):
|
|
|
|
# continue
|
|
|
|
# if not line:
|
|
|
|
# continue
|
|
|
|
# pieces = line.split()
|
|
|
|
# chunk = pieces.pop(0)
|
|
|
|
# assert chunk not in seen, chunk
|
|
|
|
# seen.add(chunk)
|
|
|
|
# entries.append((chunk, list(pieces)))
|
|
|
|
# if chunk[0].isalpha() and chunk[0].islower():
|
|
|
|
# chunk = chunk[0].title() + chunk[1:]
|
|
|
|
# pieces[0] = pieces[0][0].title() + pieces[0][1:]
|
|
|
|
# seen.add(chunk)
|
|
|
|
# entries.append((chunk, pieces))
|
|
|
|
# return entries
|
|
|
|
|
|
|
|
|
|
|
|
# def read_detoken_rules(lang): # Deprecated?
|
|
|
|
# loc = path.join(DATA_DIR, lang, 'detokenize')
|
|
|
|
# entries = []
|
|
|
|
# with utf8open(loc) as file_:
|
|
|
|
# for line in file_:
|
|
|
|
# entries.append(line.strip())
|
|
|
|
# return entries
|
2015-04-19 08:31:31 +00:00
|
|
|
|
2014-10-22 01:54:59 +00:00
|
|
|
|
2015-07-19 13:18:17 +00:00
|
|
|
def align_tokens(ref, indices): # Deprecated, surely?
|
2014-10-14 04:47:06 +00:00
|
|
|
start = 0
|
|
|
|
queue = list(indices)
|
|
|
|
for token in ref:
|
|
|
|
end = start + len(token)
|
|
|
|
emit = []
|
|
|
|
while queue and queue[0][1] <= end:
|
|
|
|
emit.append(queue.pop(0))
|
|
|
|
yield token, emit
|
|
|
|
start = end
|
|
|
|
assert not queue
|
2014-10-18 07:02:05 +00:00
|
|
|
|
|
|
|
|
2015-07-19 13:18:17 +00:00
|
|
|
def detokenize(token_rules, words): # Deprecated?
|
2015-04-19 08:31:31 +00:00
|
|
|
"""To align with treebanks, return a list of "chunks", where a chunk is a
|
2014-10-18 07:02:05 +00:00
|
|
|
sequence of tokens that are separated by whitespace in actual strings. Each
|
|
|
|
chunk should be a tuple of token indices, e.g.
|
|
|
|
|
|
|
|
>>> detokenize(["ca<SEP>n't", '<SEP>!'], ["I", "ca", "n't", "!"])
|
|
|
|
[(0,), (1, 2, 3)]
|
|
|
|
"""
|
|
|
|
string = ' '.join(words)
|
|
|
|
for subtoks in token_rules:
|
|
|
|
# Algorithmically this is dumb, but writing a little list-based match
|
|
|
|
# machine? Ain't nobody got time for that.
|
|
|
|
string = string.replace(subtoks.replace('<SEP>', ' '), subtoks)
|
|
|
|
positions = []
|
|
|
|
i = 0
|
|
|
|
for chunk in string.split():
|
|
|
|
subtoks = chunk.split('<SEP>')
|
|
|
|
positions.append(tuple(range(i, i+len(subtoks))))
|
|
|
|
i += len(subtoks)
|
|
|
|
return positions
|