anorack-0.2.7/0000755000000000000000000000000013665242436013147 5ustar00rootroot00000000000000anorack-0.2.7/.coveragerc0000644000000000000000000000014113665242431015257 0ustar00rootroot00000000000000[run] branch = true [report] show_missing = true exclude_lines = # no coverage # vim:ft=dosini anorack-0.2.7/.pylintrc0000644000000000000000000000065513665242431015015 0ustar00rootroot00000000000000[MASTER] load-plugins = pylint.extensions.check_elif [MESSAGES CONTROL] disable = bad-continuation, bad-option-value, duplicate-code, invalid-name, locally-disabled, no-else-return, too-few-public-methods, [REPORTS] reports = no score = no msg-template = {path}:{line}: {C}: {symbol} [{obj}] {msg} [FORMAT] max-line-length = 120 expected-line-ending-format = LF # vim:ft=dosini ts=4 sts=4 sw=4 et anorack-0.2.7/Makefile0000644000000000000000000000453113665242431014605 0ustar00rootroot00000000000000# Copyright © 2012-2018 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. PYTHON = python3 PREFIX = /usr/local DESTDIR = bindir = $(PREFIX)/bin basedir = $(PREFIX)/share/anorack mandir = $(PREFIX)/share/man .PHONY: all all: ; .PHONY: install install: anorack $(PYTHON) - < lib/__init__.py # Python version check # executable: install -d $(DESTDIR)$(bindir) python_exe=$$($(PYTHON) -c 'import sys; print(sys.executable)') && \ sed \ -e "1 s@^#!.*@#!$$python_exe@" \ -e "s#^basedir = .*#basedir = '$(basedir)/'#" \ $(<) > $(<).tmp install $(<).tmp $(DESTDIR)$(bindir)/$(<) rm $(<).tmp # data: install -d $(DESTDIR)$(basedir)/data install -p -m644 data/* $(DESTDIR)$(basedir)/data/ # library: install -d $(DESTDIR)$(basedir)/lib install -p -m644 lib/*.py $(DESTDIR)$(basedir)/lib/ ifeq "$(DESTDIR)" "" umask 022 && $(PYTHON) -m compileall -q $(basedir)/lib/ endif ifeq "$(wildcard doc/*.1)" "" # run "$(MAKE) -C doc" to build the manpage else # manual page: install -d $(DESTDIR)$(mandir)/man1 install -p -m644 doc/$(<).1 $(DESTDIR)$(mandir)/man1/ endif .PHONY: test test: $(PYTHON) -c 'import nose; nose.main()' --verbose .PHONY: clean clean: find . -type f -name '*.py[co]' -delete find . -type d -name '__pycache__' -delete rm -f .coverage rm -f *.tmp .error = GNU make is required # vim:ts=4 sts=4 sw=4 noet anorack-0.2.7/anorack0000755000000000000000000000253213665242431014510 0ustar00rootroot00000000000000#!/usr/bin/env python3 # encoding=UTF-8 # Copyright © 2016-2018 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import sys basedir = None if basedir is not None: sys.path[:0] = [basedir] import lib.cli # pylint: disable=wrong-import-position if __name__ == '__main__': lib.cli.main() # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/data/0000755000000000000000000000000013665242431014053 5ustar00rootroot00000000000000anorack-0.2.7/data/overrides0000644000000000000000000000020213665242431015772 0ustar00rootroot00000000000000EWMH E.W.M.H UCS U.C.S UDP U.D.P UPS U.P.S UTF U.T.F UTS U.T.S UUID U.U.I.D src source unary [[j'un@ri]] [[jˈunəɹi]] usr U.S.R anorack-0.2.7/doc/0000755000000000000000000000000013665242436013714 5ustar00rootroot00000000000000anorack-0.2.7/doc/LICENSE0000644000000000000000000000207413665242431014717 0ustar00rootroot00000000000000Copyright © 2012-2020 Jakub Wilk Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. anorack-0.2.7/doc/Makefile0000644000000000000000000000363613665242431015357 0ustar00rootroot00000000000000# Copyright © 2014-2019 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. export LC_ALL=C rst2man = $(notdir $(shell command -v rst2man || echo rst2man.py)) rst2xml = $(notdir $(shell command -v rst2xml || echo rst2xml.py)) exe = anorack .PHONY: all all: $(exe).1 $(exe).1: manpage.rst $(rst2man) --input-encoding=UTF-8 < $(<) > $(@).tmp perl -pi -e '/^[.]BI\b/ and s/\\fP/\\fR/g' $(@).tmp # work-around for https://bugs.debian.org/806601 perl -ni -e 'print unless /^[.]\\" vim:/' $(@).tmp mv $(@).tmp $(@) .PHONY: check check: check-changelog check-rst .PHONY: check-changelog check-changelog: changelog dpkg-parsechangelog -l$(<) --all 2>&1 >/dev/null | { ! grep .; } .PHONY: check-rst check-rst: ls README *.rst | xargs -t -I{} $(rst2xml) --input-encoding=UTF-8 --strict {} /dev/null .PHONY: clean clean: rm -f $(exe).1 *.tmp .error = GNU make is required # vim:ts=4 sts=4 sw=4 noet anorack-0.2.7/doc/README0000644000000000000000000000231413665242431014567 0ustar00rootroot00000000000000Overview ======== The English language has two indefinite articles: + *a*: used before words that begin with a consonant sound (e.g., *a program*, *a host*, *a user*); + *an*: used before words that begin with a vowel sound (e.g., *an example*, *an hour*, *an undefined variable*). **anorack** is a specialized spell-checker that finds incorrect indefinite articles: .. code:: console $ cat test a Ubuntu user a 8-byte word an username $ anorack test test:1: a Ubuntu -> an Ubuntu /u:b'u:ntu:/ test:2: a 8 -> an 8 /'eIt/ test:3: an username -> a username /j'u:z3n,eIm/ Prerequisites ============= * Python ≥ 3.3 * `eSpeak NG`_ or eSpeak_ ≥ 1.47.08 .. _eSpeak NG: https://github.com/espeak-ng/espeak-ng .. _eSpeak: http://espeak.sourceforge.net/ Installation ============ You can use anorack without installing it, straight out of unpacked source tarball or a VCS checkout. It's also possible to install it system-wide with:: # make install By default, ``make install`` installs the package to ``/usr/local``. You can specify a different installation prefix by setting the ``PREFIX`` variable, e.g.:: $ make install PREFIX="$HOME/.local" .. vim:ft=rst ts=3 sts=3 sw=3 et anorack-0.2.7/doc/anorack.10000644000000000000000000000355513665242436015424 0ustar00rootroot00000000000000.\" Man page generated from reStructuredText. . .TH ANORACK 1 "2020-05-28" "anorack 0.2.7" "" .SH NAME anorack \- “a” vs “an” checker . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .SH SYNOPSIS .sp \fBanorack\fP [\fIoption\fP\&...] [\fIfile\fP\&...] .SH DESCRIPTION .sp The English language has two indefinite articles: .INDENT 0.0 .IP \(bu 2 \fIa\fP: used before words that begin with a consonant sound (e.g., \fIa program\fP, \fIa host\fP, \fIa user\fP); .IP \(bu 2 \fIan\fP: used before words that begin with a vowel sound (e.g., \fIan example\fP, \fIan hour\fP, \fIan undefined variable\fP). .UNINDENT .sp \fBanorack\fP is a specialized spell\-checker that finds incorrect indefinite articles. .SH OPTIONS .INDENT 0.0 .TP .B \-\-ipa Print phonemes using IPA (International Phonetic Alphabet) instead of ASCII phoneme mnemonics. .TP .B \-h\fP,\fB \-\-help Show help message and exit. .TP .B \-\-version Show version information and exit. .UNINDENT .SH EXAMPLE .INDENT 0.0 .INDENT 3.5 .sp .nf .ft C $ cat test a Ubuntu user a 8\-byte word an username $ anorack test test:1: a Ubuntu \-> an Ubuntu /u:b\(aqu:ntu:/ test:2: a 8 \-> an 8 /\(aqeIt/ test:3: an username \-> a username /j\(aqu:z3n,eIm/ .ft P .fi .UNINDENT .UNINDENT .SH SEE ALSO .sp \fBespeak\fP(1) . .\" Generated by docutils manpage writer. . anorack-0.2.7/doc/changelog0000644000000000000000000000613113665242431015562 0ustar00rootroot00000000000000anorack (0.2.7) unstable; urgency=low * Add override for "UPS". https://github.com/jwilk/anorack/issues/6 Thanks to Martin Michlmayr for the bug report. * Fix parsing words with intra-word apostrophes. https://github.com/jwilk/anorack/issues/7 Thanks to Martin Michlmayr for the bug report. -- Jakub Wilk Mon, 01 Jun 2020 20:12:29 +0200 anorack (0.2.6) unstable; urgency=low * Add override for "src". https://github.com/jwilk/anorack/issues/5 Thanks to Paul Wise and Emmanuel Arias for the bug report. -- Jakub Wilk Mon, 25 May 2020 14:49:32 +0200 anorack (0.2.5) unstable; urgency=low * Drop support for Python 3.2. * Don't die with exception when a file cannot be opened. (If there are many input files, it's helpful to continue when one of them cannot be opened.) * Improve the build system: + Check Python version on install. (Only when DESTDIR is not set.) + Byte-compile Python code on install. + Add checks against BSD make. (Only GNU make is supported.) + Don't require GNU install(1). + Remove the test coverage file in the clean target. * Rephrase description of --version in help messages. * Improve the test suite. -- Jakub Wilk Mon, 21 Oct 2019 18:08:22 +0200 anorack (0.2.4) unstable; urgency=low * Reset the SIGPIPE signal disposition. * Improve the build system. -- Jakub Wilk Fri, 18 May 2018 21:05:06 +0200 anorack (0.2.3) unstable; urgency=low * Rewrite shebang at install time. * Make the doc makefile more portable. * Add installation instructions to README. * Improve the test suite. -- Jakub Wilk Wed, 22 Mar 2017 18:46:51 +0100 anorack (0.2.2) unstable; urgency=low * Fix compatibility with eSpeak >= 1.48.11. * Add support for eSpeak NG. * Put license into a separate file. -- Jakub Wilk Wed, 19 Oct 2016 20:11:33 +0200 anorack (0.2.1) unstable; urgency=low * Explain the grammar rules in README and in the manual page. * Don't disable stdout/stderr line buffering. -- Jakub Wilk Sun, 21 Aug 2016 21:31:00 +0200 anorack (0.2) unstable; urgency=low * Fix word-splitting for compounds that include numbers or underscores. This fixes, among others, false positives involving acronyms such as “a UTF16”. * Retain original article's case in correction. * Add option for printing phonemes using IPA (--ipa). * Add Makefile. * Use /usr/bin/env in shebang. -- Jakub Wilk Mon, 18 Jul 2016 12:12:40 +0200 anorack (0.1.1) unstable; urgency=low * Add the manual page. * Add N (ŋ) to the consonants set. * Allow quotation character between the article and the other word. * Fix false positives for the following phrases: + an EWMH + a UCS + a UDP + a UTF + a UTS + a UUID + a unary + a usr * Improve the test suite. -- Jakub Wilk Mon, 11 Jul 2016 21:42:44 +0200 anorack (0.1) unstable; urgency=low * Initial release. -- Jakub Wilk Mon, 04 Jul 2016 21:04:36 +0200 anorack-0.2.7/doc/manpage.rst0000644000000000000000000000210713665242431016051 0ustar00rootroot00000000000000======= anorack ======= ------------------- “a” vs “an” checker ------------------- :manual section: 1 :version: anorack 0.2.7 :date: 2020-05-28 Synopsis -------- **anorack** [*option*...] [*file*...] Description ----------- The English language has two indefinite articles: + *a*: used before words that begin with a consonant sound (e.g., *a program*, *a host*, *a user*); + *an*: used before words that begin with a vowel sound (e.g., *an example*, *an hour*, *an undefined variable*). **anorack** is a specialized spell-checker that finds incorrect indefinite articles. Options ------- --ipa Print phonemes using IPA (International Phonetic Alphabet) instead of ASCII phoneme mnemonics. -h, --help Show help message and exit. --version Show version information and exit. Example ------- :: $ cat test a Ubuntu user a 8-byte word an username $ anorack test test:1: a Ubuntu -> an Ubuntu /u:b'u:ntu:/ test:2: a 8 -> an 8 /'eIt/ test:3: an username -> a username /j'u:z3n,eIm/ See also -------- **espeak**\ (1) .. vim:ts=3 sts=3 sw=3 anorack-0.2.7/lib/0000755000000000000000000000000013665242431013710 5ustar00rootroot00000000000000anorack-0.2.7/lib/__init__.py0000644000000000000000000000017013665242431016017 0ustar00rootroot00000000000000''' anorack's private modules ''' type(lambda: (yield from [])) # Python >= 3.3 is required # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/articles.py0000644000000000000000000000314013665242431016066 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' English articles ''' from lib import phonetics accents = ''.join(phonetics.accents) def choose_art(phonemes): ''' choose correct article for the phonemes: return "a" or "an" or NotImplemented ''' try: p = phonemes.strip(accents)[0] except IndexError: return NotImplemented if p in phonetics.consonants: return 'a' elif p in phonetics.vowels: return 'an' else: return NotImplemented __all__ = ['choose_art'] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/cli.py0000644000000000000000000001015313665242431015031 0ustar00rootroot00000000000000# Copyright © 2016-2019 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' anorack CLI ''' import argparse import io import signal import sys from lib.articles import choose_art from lib.io import ( get_encoding, open_file, ) from lib.misc import coerce_case, warn from lib.parser import parse_file from lib.phonetics import init as init_phonetics, text_to_phonemes from lib.version import __version__ class VersionAction(argparse.Action): ''' argparse --version action ''' def __init__(self, option_strings, dest=argparse.SUPPRESS): super().__init__( option_strings=option_strings, dest=dest, nargs=0, help='show version information and exit' ) def __call__(self, parser, namespace, values, option_string=None): from lib import espeak # pylint: disable=import-outside-toplevel print('{prog} {0}'.format(__version__, prog=parser.prog)) print('+ Python {0}.{1}.{2}'.format(*sys.version_info)) print('+ eSpeak{ng} {0}'.format( espeak.version, ng=(' NG' if espeak.ng else '') )) parser.exit() def check_word(loc, art, word, *, ipa=False): ''' check if the word has correct article ''' phon = text_to_phonemes(word, ipa=ipa) correct_art = choose_art(phon) if correct_art is NotImplemented: warn("can't determine correct article for {word!r} /{phon}/".format(word=word, phon=phon)) elif art.lower() != correct_art: correct_art = coerce_case(art, correct_art) print('{loc}: {art} {word} -> {cart} {word} /{phon}/'.format( loc=loc, art=art, cart=correct_art, word=word, phon=phon, )) def main(): ''' run the program ''' signal.signal(signal.SIGPIPE, signal.SIG_DFL) ap = argparse.ArgumentParser(description='"a" vs "an" checker') ap.add_argument('--version', action=VersionAction) ap.add_argument('--ipa', action='store_true', help='use IPA instead of phoneme mnemonics') ap.add_argument('--traceback', action='store_true', help=argparse.SUPPRESS) ap.add_argument('files', metavar='FILE', nargs='*', default=['-'], help='file to check (default: stdin)') options = ap.parse_args() encoding = get_encoding() sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding, line_buffering=sys.stdout.line_buffering) sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding, line_buffering=sys.stdout.line_buffering) init_phonetics() rc = 0 for path in options.files: try: file = open_file(path, encoding=encoding, errors='replace') except OSError as exc: if options.traceback: raise msg = '{prog}: {path}: {exc}'.format(prog=ap.prog, path=path, exc=exc.strerror) print(msg, file=sys.stderr) rc = 1 continue with file: for loc, art, word in parse_file(file): check_word(loc, art, word, ipa=options.ipa) sys.exit(rc) __all__ = ['main'] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/espeak.py0000644000000000000000000001076013665242431015536 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' interface to eSpeak (NG) ''' import ctypes import distutils.version try: _shlib = ctypes.CDLL('libespeak-ng.so.1') ng = True except OSError: # no coverage _shlib = ctypes.CDLL('libespeak.so.1') ng = False # const char *espeak_Info(const char **path_data) _info = _shlib.espeak_Info _info.argtypes = [ctypes.POINTER(ctypes.c_char_p)] _info.restype = ctypes.c_char_p def info(): ''' return eSpeak version information ''' dummy = ctypes.c_char_p(b'') res = _info(ctypes.byref(dummy)) return res.decode('ASCII') version = distutils.version.LooseVersion(info().split()[0]) del info # int espeak_Initialize(espeak_AUDIO_OUTPUT output, int buflength, const char *path, int options) _initialize = _shlib.espeak_Initialize _initialize.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_int] _initialize.restype = ctypes.c_int def init(): ''' initialize eSpeak ''' rc = _initialize(0, 0, None, 0) if rc <= 0: raise RuntimeError('espeak_Initialize(): internal error') # no coverage # espeak_ERROR espeak_SetVoiceByName(const char *name) _set_voice_by_name = _shlib.espeak_SetVoiceByName _set_voice_by_name.argtypes = [ctypes.c_char_p] _set_voice_by_name.restype = ctypes.c_int def set_voice_by_name(s): ''' use this voice for synthesis ''' s = s.encode('ASCII') rc = _set_voice_by_name(s) if rc == 0: return else: # no coverage if rc == -1: msg = 'internal error' elif rc == 1: msg = 'the command could not be buffered' else: msg = 'unknown error {}'.format(rc) raise RuntimeError('espeak_SetVoiceByName(): ' + msg) if version >= '1.48.1': # const char *espeak_TextToPhonemes(const void **textptr, int textmode, int phonememode) _text_to_phonemes = _shlib.espeak_TextToPhonemes _text_to_phonemes.restype = ctypes.c_char_p _text_to_phonemes.argtypes = [ctypes.POINTER(ctypes.c_char_p), ctypes.c_int, ctypes.c_int] def text_to_phonemes(s, *, ipa=False): ''' translate text to phonemes ''' s = s.encode('UTF-8') z = ctypes.c_char_p(s) zptr = ctypes.pointer(z) assert zptr.contents is not None if version >= '1.48.11': ipa = ipa << 1 else: ipa = ipa << 4 # no coverage res = _text_to_phonemes(zptr, 1, ipa) if zptr.contents.value is not None: raise RuntimeError # no coverage return res.decode('UTF-8').strip() elif version >= '1.47.08': # no coverage # void espeak_TextToPhonemes(const void *text, char *buffer, int size, int textmode, int phonememode) _text_to_phonemes = _shlib.espeak_TextToPhonemes _text_to_phonemes.restype = ctypes.c_char_p _text_to_phonemes.argtypes = [ ctypes.c_char_p, ctypes.POINTER(ctypes.c_char), ctypes.c_int, ctypes.c_int, ctypes.c_int ] def text_to_phonemes(s, *, ipa=False): ''' translate text to phonemes ''' s = s.encode('UTF-8') bufsize = 250 buf = ctypes.create_string_buffer(bufsize) _text_to_phonemes(s, buf, bufsize, 1, ipa << 4) return buf.value.decode('UTF-8').strip() else: # no coverage raise RuntimeError('eSpeak >= 1.47.08 is required') __all__ = [ 'init', 'ng', 'set_voice_by_name', 'text_to_phonemes', 'version', ] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/io.py0000644000000000000000000000376513665242431014704 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' I/O and encodings ''' import codecs import io import sys def enc_eq(e1, e2): ''' check if two encodings are equal ''' return ( codecs.lookup(e1).name == codecs.lookup(e2).name ) def get_encoding(): ''' get locale encoding (from sys.stdout); upgrade ASCII to UTF-8 ''' locale_encoding = sys.stdout.encoding if enc_eq(locale_encoding, 'ASCII'): return 'UTF-8' else: return locale_encoding def open_file(path, *, encoding, errors): ''' open() with special case for "-" ''' if path == '-': return io.TextIOWrapper( sys.stdin.buffer, encoding=encoding, errors=errors, ) else: return open( path, 'rt', encoding=encoding, errors=errors, ) __all__ = [ 'get_encoding', 'open_file', ] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/misc.py0000644000000000000000000000343013665242431015215 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' miscellanea ''' import os import sys def warn(msg): ''' print warning message ''' prog = os.path.basename(sys.argv[0]) print('{prog}: warning: {msg}'.format(prog=prog, msg=msg), file=sys.stderr) def _coerce_case(src, word): ''' coerce word to the same case as src (simple version doesn't support title-case) ''' if src.isupper(): return word.upper() else: return word.lower() def coerce_case(src, word): ''' coerce word to the same case as src ''' return ( _coerce_case(src[:1], word[:1]) + _coerce_case(src[1:], word[1:]) ) __all__ = [ 'warn', 'coerce_case', ] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/parser.py0000644000000000000000000000413213665242431015556 0ustar00rootroot00000000000000# Copyright © 2016-2020 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' English parser ''' import re class Location(): ''' location in a file ''' def __init__(self, file, lineno): self.file = file self.lineno = lineno def __str__(self): return '{path}:{n}'.format(path=self.file.name, n=self.lineno) find_articles = re.compile( r'''(?,
, ) tuples ''' carry = '' for i, line in enumerate(file, start=1): cline = carry + line carry = '' for match in find_articles(cline): art, word, eol_art = match.groups() if art is not None: assert word is not None yield (Location(file, i), art, word) else: assert eol_art is not None carry = eol_art + ' ' __all__ = ['parse_file'] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/phonetics.py0000644000000000000000000000476413665242431016271 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ''' English phonetics ''' import functools import os consonants = frozenset( 'DNSTZbdfghjklmnprstvwz' 'ðŋʃθʒbdfɡhjkɬmnpɹstvwz' ) vowels = frozenset( '03@AEIOUVaeiou' 'ɒɜəɑɛɪɔʊʌɐeiɔu' ) accents = frozenset( ",'" "ˌˈ" ) espeak = None overrides = {} def init(): ''' initialize underlying speech engine ''' global espeak # pylint: disable=global-statement from lib import espeak # pylint: disable=redefined-outer-name,import-outside-toplevel espeak.init() espeak.set_voice_by_name('en') here = os.path.dirname(__file__) # Ideally false positives should be fixed in eSpeak, # but as a stop-gap measure, we carry data file to correct some of them. path = '{here}/../data/overrides'.format(here=here) with open(path, 'rt', encoding='UTF-8') as file: for line in file: line = line.strip() (word, phon) = line.split('\t', 1) word = word.lower() overrides[word] = phon @functools.lru_cache(maxsize=9999) def text_to_phonemes(s, *, ipa=False): ''' translate text to phonemes ''' s = overrides.get(s.lower(), s) if s.startswith('[[') and s.endswith(']]'): return s.split('\t')[ipa][2:-2] else: return espeak.text_to_phonemes(s, ipa=ipa) __all__ = [ 'consonants', 'vowels', 'accents', 'text_to_phonemes', ] # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/lib/version.py0000644000000000000000000000005713665242431015751 0ustar00rootroot00000000000000'''anorack's version''' __version__ = '0.2.7' anorack-0.2.7/private/0000755000000000000000000000000013665242436014621 5ustar00rootroot00000000000000anorack-0.2.7/private/run-pylint0000755000000000000000000000320413665242431016662 0ustar00rootroot00000000000000#!/bin/sh # Copyright © 2015-2018 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. set -e -u PYTHON=${PYTHON:-python3} "$PYTHON" -m pylint --version >/dev/null || exit 1 if [ $# -eq 0 ] then pyscripts=$(grep -l -r '^#!.*python' .) set -- lib tests $pyscripts fi if [ -n "${VIRTUAL_ENV:-}" ] then # https://github.com/PyCQA/pylint/issues/73 set -- --ignored-modules=distutils "$@" fi log=$(mktemp -t pylint.XXXXXX) "$PYTHON" -m pylint "$@" > "$log" || [ $? != 1 ] ! grep -P '^\S+:' "$log" \ | grep -v -P '^(?!lib/).*: missing-(\w+-)?docstring ' \ | grep '.' || exit 1 rm "$log" # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/private/update-coverage0000755000000000000000000000423413665242431017620 0ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright © 2014 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import io import os import sys import nose import nose.plugins.cover class Coverage(nose.plugins.cover.Coverage): stream = None def report(self, stream): return super().report(self.stream) basedir = os.path.join( os.path.dirname(__file__), os.pardir, ) def main(): argv = [ sys.argv[0], '--with-coverage', '--cover-package=lib', '--cover-erase', ] path = os.path.join( 'tests', 'coverage' ) plugin = Coverage() report_stream = plugin.stream = io.StringIO() print( 'Generated automatically by private/update-coverage. ' 'Do not edit.\n', file=report_stream ) ok = nose.run(argv=argv, plugins=[plugin]) if not ok: sys.exit(1) report_stream.seek(0) with open(path + '.tmp', 'wt', encoding='ASCII') as file: for line in report_stream: line = line.rstrip() print(line, file=file) os.rename(path + '.tmp', path) if __name__ == '__main__': main() # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/private/update-version0000755000000000000000000000054713665242431017515 0ustar00rootroot00000000000000#!/bin/sh set -e -u version=${1:?"no version number provided"} date="$(date -u --rfc-3339=date)" PS4='$ ' set -x dch -m -v "$version" -u low -c doc/changelog export version date perl -pi -e 's/^__version__ = '"'"'\K[\w.]+/$ENV{version}/' lib/version.py perl -pi -e 's/^:version: \S+ \K[\w.]+/$ENV{version}/; s/^(:date:) \K[0-9-]+/$ENV{date}/' doc/manpage.rst anorack-0.2.7/tests/0000755000000000000000000000000013665242431014304 5ustar00rootroot00000000000000anorack-0.2.7/tests/__init__.py0000644000000000000000000000004513665242431016414 0ustar00rootroot00000000000000type(...) # Python >= 3 is required anorack-0.2.7/tests/coverage0000644000000000000000000000143313665242431016023 0ustar00rootroot00000000000000Generated automatically by private/update-coverage. Do not edit. Name Stmts Miss Branch BrPart Cover Missing -------------------------------------------------------------- lib/__init__.py 1 0 2 1 67% 5->exit lib/articles.py 14 0 4 0 100% lib/cli.py 56 0 10 0 100% lib/espeak.py 44 0 0 0 100% lib/io.py 16 0 4 0 100% lib/misc.py 13 0 2 0 100% lib/parser.py 22 0 6 0 100% lib/phonetics.py 27 0 4 0 100% lib/version.py 1 0 0 0 100% -------------------------------------------------------------- TOTAL 194 0 32 1 99% anorack-0.2.7/tests/test_choose_art.py0000644000000000000000000000326513665242431020051 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from nose.tools import ( assert_equal, ) import lib.articles as M def test_choose_a(): art = M.choose_art("sp'am") assert_equal(art, 'a') def test_choose_a_ipa(): art = M.choose_art("θˈɜːmɪdˌɔː") assert_equal(art, 'a') def test_choose_an(): art = M.choose_art("'Eg") assert_equal(art, 'an') def test_choose_an_ipa(): art = M.choose_art("ˈɛɡ") assert_equal(art, 'an') def test_choose_other(): art = M.choose_art('%') assert_equal(art, NotImplemented) art = M.choose_art('') assert_equal(art, NotImplemented) # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_cli.py0000644000000000000000000001446113665242431016472 0ustar00rootroot00000000000000# Copyright © 2016-2019 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import contextlib import errno import io import os import sys import tempfile import unittest.mock from nose.tools import ( assert_equal, assert_is_instance, assert_not_equal, ) from tests.tools import isolation @contextlib.contextmanager def tmpcwd(): with tempfile.TemporaryDirectory(prefix='anorack.tests.') as tmpdir: orig_cwd = os.getcwd() os.chdir(tmpdir) try: yield finally: os.chdir(orig_cwd) def TextIO(s=None, *, name): fp = io.BytesIO(s) fp.name = name return io.TextIOWrapper(fp, encoding='UTF-8') class CompletedProcess(): def __init__(self, rc, stdout, stderr): self.rc = rc self.stdout = stdout self.stderr = stderr def __run_main(argv, stdin): sys.argv = argv if stdin is not None: if isinstance(stdin, str): stdin = stdin.encode('UTF-8') sys.stdin = mock_stdin = TextIO(stdin, name=sys.__stdin__.name) else: mock_stdin = None sys.stdout = mock_stdout = TextIO(name=sys.__stdout__.name) sys.stderr = mock_stderr = TextIO(name=sys.__stderr__.name) import lib.cli # pylint: disable=bad-option-value,import-outside-toplevel rc = 0 try: lib.cli.main() except SystemExit as exc: rc = exc.code except OSError as exc: rc = exc yield rc for fp in (sys.stdout, sys.stderr): fp.flush() s = fp.buffer.getvalue() # pylint: disable=no-member yield s.decode('UTF-8') del mock_stdin, mock_stdout, mock_stderr def _run_main(argv, stdin): # abuse mock to save&restore sys.argv, sys.stdin, etc.: with unittest.mock.patch.multiple(sys, argv=None, stdin=None, stdout=None, stderr=None): return CompletedProcess(*__run_main(argv, stdin)) run_main = isolation(_run_main) def t(*, stdin=None, files=None, stdout, stdout_ipa=None, stderr='', stderr_ipa=None): if stdout_ipa is None: stdout_ipa = stdout if stderr_ipa is None: stderr_ipa = stderr argv = ['anorack'] if files is not None: for (name, content) in files: with open(name, 'wt', encoding='UTF-8') as file: file.write(content) argv += [name] actual = run_main(argv, stdin) if '-@' in stdout: stdout = stdout.replace('-@', '@') actual.stdout = actual.stdout.replace('-@', '@') assert_equal(stdout, actual.stdout) assert_equal(stderr, actual.stderr) assert_equal(actual.rc, 0) argv += ['--ipa'] actual = run_main(argv, stdin) actual.stderr = actual.stderr.replace('t͡ʃ', 'tʃ') assert_equal(stdout_ipa, actual.stdout) assert_equal(stderr_ipa, actual.stderr) assert_equal(actual.rc, 0) def test_stdin(): t( stdin=( 'It could be carried by an African swallow!\n' 'Oh, yeah, a African swallow maybe, but not an\n' 'European swallow.\n' ), stdout=( ":2: a African -> an African /'afrIk@n/\n" ":3: an European -> a European /j,U@r-@p'i@n/\n" ), stdout_ipa=( ":2: a African -> an African /ˈafɹɪkən/\n" ":3: an European -> a European /jˌʊəɹəpˈiən/\n" ), ) @tmpcwd() def test_files(): t( files=( ('holy', 'It could be carried by a African swallow!'), ('grail', 'Oh, yeah, an African swallow maybe, but not an European swallow.'), ), stdout=( "holy:1: a African -> an African /'afrIk@n/\n" "grail:1: an European -> a European /j,U@r-@p'i@n/\n" ), stdout_ipa=( "holy:1: a African -> an African /ˈafɹɪkən/\n" "grail:1: an European -> a European /jˌʊəɹəpˈiən/\n" ), ) def test_warning(): def dummy_choose_art(phon): # pylint: disable=unused-argument return NotImplemented with unittest.mock.patch('lib.cli.choose_art', dummy_choose_art): t( stdin='A scratch?!', stdout='', stderr="anorack: warning: can't determine correct article for 'scratch' /skr'atS/\n", stderr_ipa="anorack: warning: can't determine correct article for 'scratch' /skɹˈatʃ/\n", ) def test_bad_io(): argv = ['anorack', '/nonexistent', '-'] actual = run_main(argv, 'a African') assert_equal(':', actual.stdout[:8]) stderr = '{prog}: {path}: {err}\n'.format( prog=argv[0], path=argv[1], err=os.strerror(errno.ENOENT) ) assert_equal(stderr, actual.stderr) assert_equal(actual.rc, 1) argv[1:1] = ['--traceback'] actual = run_main(argv, 'a African') assert_equal('', actual.stdout) assert_equal('', actual.stderr) assert_is_instance(actual.rc, OSError) assert_equal(actual.rc.errno, errno.ENOENT) def test_changelog(): argv = ['anorack', 'doc/changelog'] actual = run_main(argv, None) assert_equal('', actual.stdout) assert_equal('', actual.stderr) assert_equal(actual.rc, 0) def test_version(): argv = ['anorack', '--version'] actual = run_main(argv, None) assert_not_equal('', actual.stdout) assert_equal('', actual.stderr) assert_equal(actual.rc, 0) # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_coerce_case.py0000644000000000000000000000301713665242431020151 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. from nose.tools import ( assert_equal, ) import lib.misc as M def t(src, word, exp): res = M.coerce_case(src, word) assert_equal(exp, res) def test_a(): t('a', 'a', 'a') t('A', 'a', 'A') t('an', 'a', 'a') t('An', 'a', 'A') t('AN', 'a', 'A') def test_an(): t('a', 'an', 'an') t('A', 'an', 'An') t('an', 'an', 'an') t('An', 'an', 'An') t('AN', 'an', 'AN') # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_get_encoding.py0000644000000000000000000000312313665242431020341 0ustar00rootroot00000000000000# Copyright © 2016-2018 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import unittest.mock from nose.tools import ( assert_equal, ) import lib.io as M def t(src, dst): class mock_stdout: encoding = src with unittest.mock.patch('sys.stdout', mock_stdout): encoding = M.get_encoding() assert_equal(encoding, dst) def test_ascii(): t('ANSI_X3.4-1968', 'UTF-8') t('US-ASCII', 'UTF-8') t('ASCII', 'UTF-8') def test_8bit(): t('ISO-8859-2', 'ISO-8859-2') def test_utf8(): t('UTF-8', 'UTF-8') # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_open_file.py0000644000000000000000000000277313665242431017666 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import os from nose.tools import ( assert_equal, ) import lib.io as M def t(path): encoding = 'ISO-8859-2' errors = 'xmlcharrefreplace' with M.open_file(path, encoding=encoding, errors=errors) as file: assert_equal(file.encoding, encoding) assert_equal(file.errors, errors) def test_open_real_file(): t(os.devnull) def test_open_stdin(): t('-') # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_parse_file.py0000644000000000000000000000500013665242431020021 0ustar00rootroot00000000000000# Copyright © 2016-2020 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import io from nose.tools import ( assert_equal, assert_is, assert_is_instance, ) import lib.parser as M def t(s, exp): if isinstance(exp, tuple): exp = [exp] file = io.StringIO(s) result = list(M.parse_file(file)) assert_equal(len(result), len(exp)) for (loc, art, word), (xi, xart, xword) in zip(result, exp): assert_is_instance(loc, M.Location) assert_is(loc.file, file) assert_equal(loc.lineno, xi) assert_equal(xart, art) assert_equal(xword, word) def test_mid_line(): t('Oh, yeah, an African swallow maybe,\nbut not a European swallow.\n', [ (1, 'an', 'African'), (2, 'a', 'European'), ]) def test_wrapped(): t('I thought we were an\nautonomous collective.', (2, 'an', 'autonomous')) def test_quotes(): t( "a 'scratch'\n" 'a ‘scratch’\n' 'a "scratch"\n' 'a “scratch”\n', [(i, 'a', 'scratch') for i in range(1, 5)] ) def test_underscore(): t('a European_swallow', (1, 'a', 'European')) def test_numbers(): t('an 8', (1, 'an', '8')) t('an 8bit', (1, 'an', '8')) t('an 8-bit', (1, 'an', '8')) t('an 8 bit', (1, 'an', '8')) t('a UTF16', (1, 'a', 'UTF')) t('a UTF-16', (1, 'a', 'UTF')) t('a UTF 16', (1, 'a', 'UTF')) def test_inner_apostrophe(): t("Esra'a Al Shafei", []) t('Esra’a Al Shafei', []) # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_phonetics.py0000644000000000000000000000430213665242431017710 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import functools from nose.tools import ( assert_equal, ) from tests.tools import isolation import lib.phonetics as M def __test(word, xphon, xipa): M.init() phon = M.text_to_phonemes(word) assert_equal(xphon, phon) ipa = M.text_to_phonemes(word, ipa=True) ipa = ipa.replace('t͡ʃ', 'tʃ') assert_equal(xipa, ipa) _test = isolation(__test) def test_overrides(): def t(word, xphon, xipa): return ( functools.partial(_test, xphon=xphon, xipa=xipa), word ) yield t('EWMH', ",i:d,Vb@Lj,u:,Em'eItS", 'ˌiːdˌʌbəljˌuːˌɛmˈeɪtʃ') yield t('UCS', "j,u:s,i:;'Es", 'jˌuːsˌiːˈɛs') yield t('UDP', "j,u:d,i:p'i:", 'jˌuːdˌiːpˈiː') yield t('UPS', "j,u:p,i:;'Es", 'jˌuːpˌiːˈɛs') yield t('UTF', "j,u:t,i:;'Ef", 'jˌuːtˌiːˈɛf') yield t('UTS', "j,u:t,i:;'Es", 'jˌuːtˌiːˈɛs') yield t('UUID', "j,u:j,u:,aId'i:", 'jˌuːjˌuːˌaɪdˈiː') yield t('src', "s'o@s", 'sˈɔːs') yield t('unary', "j'un@ri", "jˈunəɹi") yield t('usr', "j,u:,Es'A@", "jˌuːˌɛsˈɑː") # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_version.py0000644000000000000000000000356713665242431017415 0ustar00rootroot00000000000000# Copyright © 2012-2018 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import os from nose.tools import ( assert_equal, ) from lib.version import __version__ here = os.path.dirname(__file__) docdir = os.path.join(here, os.pardir, 'doc') def test_changelog(): path = os.path.join(docdir, 'changelog') with open(path, 'rt', encoding='UTF-8') as file: line = file.readline() changelog_version = line.split()[1].strip('()') assert_equal(changelog_version, __version__) def test_manpage(): path = os.path.join(docdir, 'manpage.rst') manpage_version = None with open(path, 'rt', encoding='UTF-8') as file: for line in file: if line.startswith(':version:'): manpage_version = line.split()[-1] break assert_equal(manpage_version, __version__) # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/test_warn.py0000644000000000000000000000307113665242431016665 0ustar00rootroot00000000000000# Copyright © 2016-2018 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import io import unittest.mock from nose.tools import ( assert_equal, ) import lib.misc as M def test_warn(): stderr = io.StringIO() with unittest.mock.patch('sys.stderr', stderr): with unittest.mock.patch('sys.argv', ['/usr/bin/anorack']): M.warn('NOBODY expects the Spanish Inquisition!') assert_equal( stderr.getvalue(), 'anorack: warning: NOBODY expects the Spanish Inquisition!\n' ) # vim:ts=4 sts=4 sw=4 et anorack-0.2.7/tests/tools.py0000644000000000000000000000316413665242431016022 0ustar00rootroot00000000000000# Copyright © 2016 Jakub Wilk # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import concurrent.futures import functools import sys def isolation(f): if 'coverage' in sys.modules: # Process isolation would break coverage measurements. # Oh well. FIXME. return f else: @functools.wraps(f) def wrapper(*args, **kwargs): with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor: ftr = executor.submit(f, *args, **kwargs) return ftr.result() return wrapper __all__ = ['isolation'] # vim:ts=4 sts=4 sw=4 et