commit f3093f293eda954792311473bc489a0700fa3d15 Author: Lokendra Singh Date: Mon Apr 15 00:53:02 2019 +0530 Add setup.py (#38) * Installation script for windows platform * Removed Dependency on Apertium Bash helper script to make compatible with windows * Modifying the environment varible PATH for the current process only * Update the location of Language files for Windows Environment by checking at default installation path * Use installation.py to install on windows platform * Fix: Windows Installation Check * Completed the incomplete comment * Combine the import statements to improve readability * Renamed * Implemented changes suggested in class renamed: class Installation -> Installer added: object variable _language_link renamed: main() -> install_apertium_windows() * Added: Logging Module * Added: tempfile module to create temp directory * Removed: Multiple spaces after operator * Added: Function type annotation * Replaced: fstring with format method to support previous python version * Implemented changes suggested modified: opening README.MD removed: redundant setup_requires added: python_requirement >= 3.4 * Added: Function type annotation * Added: Newline after stdlib * Implemented changes suggested Removed: from os import path Edited: Process' Path comment * Implemented changes suggested Sorted the import libraries Added: TODO variables * Implemented changes suggested Sorted the import statements Edited: import shutil Removed: from os import path Added: class variable with base link for downloading Edited: with open instead of open Edited: logging messages Removed: blank lines around class definition Renamed: mode_editor() -> edit_modes() Removed: Redundant if __name__ * Implemented changes suggested Renamed: download_language_data() -> download_package() Edited: contructor parameter from tuple to list * added: class method install to call all the relevant methods * Added: python setup.py test * Added: python setup.py install This should verify the installation process on Appveyor * Replaced: os.putenv, os.getenv os.environ * Added: ';' while updating path values generate logs while updating path * Undo all the changes done in utils.py * Add the Apertium Binaries to Process' PATH while importing module * Replaced: Single quotes with Double quotes in Docstring * Implemented changes suggested - Replaced: Single Quotes with Double Quotes - Removed: Redundant Blank line - Fixed: type annotation comment for Installer._download_zip() - Fixed: Typo in comment - Renamed: zip_obj -> zip_file - Replaced: download_dir, extract_path -> self._download_path, self._install_path - Removed: redundant argument 'download_dir' from Installer._download_zip() - Modified: Use Installer.base_link as download link for apertium_windows - Replaced: file with f, as 'file' messes up with syntax highlighting - Removed: Redundant read mode while opening a file - Renamed: Installer.install() -> Installer.install_windows() * Implemented changes suggested - Added: TODO values - Removed: redundant spaces around operator while calling setup() * Removed redundant code and renamed class method - Removed: Redundant removal of single quotes from .mode files - Renamed: Installer methods to suggest internal usage * Replaced: ';' with os.pathsep * Implemented changes suggested - Replaced: '\' with os.sep - Renamed: Installer._download_zip() -> Installer._download_zips() - Removed: New Line at start of Installer._download_zips() - Modified: Installer.base_link is modified in _download_zips() - Modified: Used 'with' while opening zip_file in _download_zips() - Raise ValueError when setup isn't run on Windows Platform - Modified: Send logs to stdout * Implemented changes suggested - Removed: Redundant ZipFile.close method(with context manager) - Merged Lines: key and value in a single line, in apertium_windows{} diff --git a/.appveyor.yml b/.appveyor.yml index 1c2354f..2535a70 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -34,6 +34,7 @@ install: - python --version - python -c "import sys,platform,struct; print(sys.platform, platform.machine(), struct.calcsize('P') * 8, )" - pip install -r requirements-dev.txt + - python setup.py install build: false test_script: - coverage run -m unittest --verbose --buffer tests diff --git a/apertium/__init__.py b/apertium/__init__.py index 66ac4c2..22c6b8b 100644 --- a/apertium/__init__.py +++ b/apertium/__init__.py @@ -1,3 +1,6 @@ +import platform +import os + from apertium.mode_search import search_path from apertium.analysis import Analyzer, analyze # noqa: F401 from apertium.generation import Generator, generate # noqa: F401 @@ -38,9 +41,33 @@ def append_pair_path(pair_path): # type: (str) -> None _update_modes(pair_path) +def append_pair_path_windows(): # type (None) -> None + if platform.system() == 'Windows': + install_path = os.getenv('LOCALAPPDATA') + apertium_lang_path = \ + os.path.join(install_path, 'apertium-all-dev', 'share', 'apertium') + if os.path.isdir(apertium_lang_path): + append_pair_path(apertium_lang_path) + + +def update_path_windows(): + """Adding the Apertium Binaries to Process' PATH""" + + if platform.system() == 'Windows': + install_path = os.environ['LOCALAPPDATA'] + current = os.environ['path'] + + apertium_path = os.path.join(install_path, 'apertium-all-dev', 'bin') + if os.path.isdir(apertium_path): + update_path = '{}{}{}{}'.format(current, os.pathsep, apertium_path, os.pathsep) + os.environ['path'] = update_path + + pair_paths = ['/usr/share/apertium', '/usr/local/share/apertium'] analyzers = {} # type: Dict[str, Tuple[str, str]] generators = {} # type: Dict[str, Tuple[str, str]] pairs = {} # type: Dict[str, str] for pair_path in pair_paths: _update_modes(pair_path) +append_pair_path_windows() +update_path_windows() diff --git a/apertium/analysis/__init__.py b/apertium/analysis/__init__.py index 1ca3fb6..db19581 100644 --- a/apertium/analysis/__init__.py +++ b/apertium/analysis/__init__.py @@ -60,8 +60,8 @@ class Analyzer: Returns: List[LexicalUnit] """ - commands = [['apertium', '-d', self.path, '-f', formatting, self.mode]] - result = execute(in_text, commands) + apertium_des = execute(in_text, [['apertium-des{}'.format(formatting), '-n']]) + result = execute(apertium_des, self._get_commands()) return self._postproc_text(result) diff --git a/installer.py b/installer.py new file mode 100644 index 0000000..929e1f8 --- /dev/null +++ b/installer.py @@ -0,0 +1,106 @@ +import logging +import os +import platform +import shutil +import tempfile +from distutils.dir_util import copy_tree +from urllib.request import urlretrieve +from zipfile import ZipFile + +class Installer: + base_link = "http://apertium.projectjj.com/{}" + + def __init__(self, languages): # type: (Installer, list) -> None + self._install_path = os.getenv("LOCALAPPDATA") + self._apertium_path = os.path.join(self._install_path, "apertium-all-dev") + self._download_path = tempfile.mkdtemp() + self._languages = languages + logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG) + self._logger = logging.getLogger() + self._logger.setLevel(logging.DEBUG) + + def _download_zips(self, download_files, extract_path): # type: (Installer, dict, str) -> None + for zip_name, zip_link in download_files.items(): + zip_download_path = os.path.join(self._download_path, zip_name) + urlretrieve(Installer.base_link.format(zip_link), filename=zip_download_path) + self._logger.info("%s download completed", zip_name) + + # Extract the zip + with ZipFile(zip_download_path) as zip_file: + zip_file.extractall(path=extract_path) + self._logger.info("%s Extraction completed", zip_name) + os.remove(zip_download_path) + self._logger.info("%s removed", zip_name) + + def _download_apertium_windows(self): # type: (Installer) -> None + """Installs Apertium-all-dev to %localappdata%""" + + apertium_windows = { + "apertium-all-dev.zip": "/win64/nightly/apertium-all-dev.zip", + } + + self._download_zips(apertium_windows, self._install_path) + + def _download_package(self): # type: (Installer) -> None + """Installs Language Data to Apertium""" + + if platform.system() == "Windows": + zip_path = "win32/nightly/data.php?zip=" + else: + raise ValueError("Installation for {} is not supported".format(platform.system())) + language_zip = {} + for curr_lang in self._languages: + language_zip[curr_lang] = zip_path + curr_lang + + self._download_zips(language_zip, self._download_path) + + # move the extracted files to desired location + lang_data_path = os.path.join(self._download_path, "usr", "share", "apertium") + + self._logger.info("Copying Language Data to Apertium") + for directory in os.listdir(lang_data_path): + source = os.path.join(lang_data_path, directory) + destination = os.path.join(self._apertium_path, "share", "apertium", directory) + copy_tree(source, destination) + self._logger.info("%s -> %s", source, destination) + + shutil.rmtree(os.path.join(self._download_path, "usr")) + + def _edit_modes(self): # type: (Installer) -> None + """The mode files need to be modified before being used on Windows System + + 1. Replace /usr/share with %localappdata%\apertium-all-dev\share + 2. Replace "/" with "\" to make path compatible with Windows System + """ + + # List of Mode Files + mode_path = os.path.join(self._apertium_path, "share", "apertium", "modes") + for f in os.listdir(mode_path): + if os.path.isfile(os.path.join(mode_path, f)) and f.endswith(".mode"): + self._logger.info("Editing mode %s ", f) + with open(os.path.join(mode_path, f)) as infile: + line = infile.read() + + contents = line.split(" ") + # Editing mode file to be compatible with windows platform + for i, t in enumerate(contents): + if len(t) > 2 and t[0] == "'" and t[1] == "/": + t = t.replace("/", os.sep) + t = t.replace(r"\usr", self._apertium_path) + contents[i] = t + line = " ".join(contents) + with open(os.path.join(mode_path, f), "w") as outfile: + outfile.write(line) + outfile.close() + + def install_windows(self): + self._download_apertium_windows() + self._download_package() + self._edit_modes() + +def install_apertium_windows(): + """Download ApertiumWin64 and Move to %localappdata%""" + + if platform.system() == "Windows": + p = Installer(["apertium-eng", "apertium-en-es"]) + p.install_windows() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b578ece --- /dev/null +++ b/setup.py @@ -0,0 +1,35 @@ +from atexit import register +from os import path +from setuptools import setup, find_packages +from setuptools.command.install import install + +import installer + + +class PostInstallCommand(install): + def __init__(self, *args, **kwargs): + super(PostInstallCommand, self).__init__(*args, **kwargs) + register(self._post_install) + + @staticmethod + def _post_install(): + installer.install_apertium_windows() + + +setup( + name='apertium-python', + # TODO: Add version description, keywords, author, author_email + license='GNU General Public License v3.0 ', + long_description=open(path.join(path.abspath(path.dirname(__file__)), 'README.md')).read(), + long_description_content_type='text/markdown; charset=UTF-8', + url='https://github.com/apertium/apertium-python', + python_requires='>=3.4', + install_requires=[ + 'apertium-streamparser==5.0.2', + ], + test_suite='tests', + packages=find_packages(exclude=['tests']), + cmdclass={ + 'install': PostInstallCommand, + }, +)