From 0425368b7dcc5a69d19ac05b859720c27e592c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Thu, 5 Jan 2017 16:30:36 +0800 Subject: [PATCH] Revised the installation instruction in README.rst. Wrote docstring in the obasync python source. Added copyright notice in the beginning of setup.py and obasync. Renamed ooexec to soffice in several places. Advanced to version 0.3. --- README.rst | 186 ++++++++++++++++++++++---- bin/obasync | 377 +++++++++++++++++++++++++++++++++++++++------------- setup.py | 29 +++- 3 files changed, 468 insertions(+), 124 deletions(-) diff --git a/README.rst b/README.rst index 879f558..6e38585 100644 --- a/README.rst +++ b/README.rst @@ -23,7 +23,7 @@ macros: If the Basic library ``MyApp`` does not exist, it will be created. Missing modules will be added, and excess modules will be removed. -And vice versa. Given the following Basic macros: +On the other hand, given the following Basic macros: * Library: ``MyApp`` * Modules: ``MyMacros`` ``Utils`` ``Registry`` ``Data`` @@ -41,42 +41,180 @@ deleted. INSTALL ------- -You can install ``obasync`` with ``pip``. Uses the ``python`` -executable that comes with your OpenOffice/LibreOffice installation -when possible. +You can either: -* OpenOffice 4 on Linux:: +1. Install ``obasync`` with ``pip`` (recommended), or - /opt/openoffice4/program/python `which pip` install obasync +2. Download the ``obasync`` script manually, and run it with the + Python that come with your OpenOffice/LibreOffice installation. -* LibreOffice 5.x on Linux:: +We will explain them in detail. - /opt/libreoffice5.*/program/python `which pip` install obasync -* Linux vendor OpenOffice/LibreOffice installation:: +OpenOffice/LibreOffice That Comes with Your Linux +################################################# + +Install with ``pip`` (Recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Your system may already have ``pip`` installed. If not, install the +``python-pip`` package from the system package manager. Then, run:: pip install obasync -* OpenOffice on Mac OS X relies on the system ``python`` - installation:: +Download and Install Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: + + python obasync + +Or, you can edit the script and change the first line (shebang) to:: + + #! /usr/bin/python + +and save this script somewhere in your path, say, ``/usr/local/bin``. +Then you can run ``obasync``. + + +OpenOffice 4 on Linux +##################### + +Install with ``pip`` (Recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Install ``pip`` for your OpenOffice installation, and then install +``obasync`` with this ``pip``:: + + wget https://bootstrap.pypa.io/get-pip.py + sudo /opt/openoffice4/program/python get-pip.py + /opt/openoffice4/program/python-core-2.7.6/bin/pip install obasync + +Download and Install Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: + + /opt/openoffice4/program/python obasync + +Or, you can edit the script and change the first line (shebang) to:: + + #! /opt/openoffice4/program/python + +and save this script somewhere in your path, say, ``/usr/local/bin``. +Then you can run ``obasync``. + + +LibreOffice on Linux +#################### + +Python from LibreOffice on Linux does not install ``pip`` properly. +However, you can still download and install ``obasync`` manually. + +Install with ``pip`` (Recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: + + /opt/libreoffice5.2/program/python obasync + +Or, you can edit the script and change the first line (shebang) to:: + + #! /opt/libreoffice5.2/program/python + +and save this script somewhere in your path, say, ``/usr/local/bin``. +Then you can run ``obasync``. + + +OpenOffice on MS-Windows +######################## + +You can install ``obasync`` with ``pip``, but the result is messy. +The recommended way is to download and install ``obasync`` manually. + +Download and Install Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: + + "C:\Program Files (x86)\OpenOffice 4\program\python.exe" obasync + + +LibreOffice on MS-Windows +######################### + +You can install ``obasync`` with ``pip``, but the result is messy. +The recommended way is to download and install ``obasync`` manually. + +Download and Install Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: + + "C:\Program Files\LibreOffice 5\program\python.exe" obasync + + +OpenOffice on Mac OS X +###################### + +Install with ``pip`` (Recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Install ``pip`` first, and then install ``obasync`` with ``pip``:: + + wget https://bootstrap.pypa.io/get-pip.py + sudo python get-pip.py sudo pip install obasync -* LibreOffice on Mac OS X: There is no simple ``pip`` way to install - ``obasync``. However, you can still download the script and run - it with the ``python`` executable that comes with your LibreOffice - installation. See below. +Download and Install Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* OpenOffice/LibreOffice on MS-Windows: You can still install - ``obasync`` with ``pip`` from your OpenOffice/LibreOffice - installation. However, it would be much easier to just download the - script and run it with the ``python`` executable that comes with - your OpenOffice/LibreOffice installation. See below. +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: -To download the package or the source script: + python obasync -* Python package: https://pypi.python.org/pypi/obasync -* Source directory: https://github.com/imacat/obasync +Or, you can edit the script and change the first line (shebang) to:: + + #! /usr/bin/python + +and save this script somewhere in your path, say, ``/usr/local/bin``. +Then you can run ``obasync``. + + + +LibreOffice on Mac OS X +####################### + +Python from LibreOffice on Mac OS X does not install ``pip`` properly. +However, you can still download and install ``obasync`` manually. + +Download and Install Manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Download ``obasync`` from either `PyPI +`_ or `GitHub +`_. Then, run ``obasync`` as:: + + /Applications/LibreOffice.app/Contents/Resources/python obasync + +Or, you can edit the script and change the first line (shebang) to:: + + #! /Applications/LibreOffice.app/Contents/Resources/python + +and save this script somewhere in your path, say, ``/usr/local/bin``. +Then you can run ``obasync``. OPTIONS @@ -120,7 +258,7 @@ LIBRARY The name of the Basic library. Default to the same -h, --help Show the help message and exit --v, --version Show program's version number and exit +-v, --version Show program’s version number and exit COPYRIGHT diff --git a/bin/obasync b/bin/obasync index f5f513d..0d867c0 100755 --- a/bin/obasync +++ b/bin/obasync @@ -3,6 +3,94 @@ # Office Basic macro source synchronizer. # by imacat , 2016-08-31 +# Copyright (c) 2016 imacat. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Synchronize Office Basic macro source. + +obasync is an OpenOffice/LibreOffice Basic macro source synchronizer. +It synchronizes your Basic macros with your local project files. + +Given the following source files: + +* Directory: MyApp +* Files: MyMacros.vb Utils.vb Registry.vb Data.vb + +Running "obasync" will synchronize them with the following Basic +macros: + +* Library: MyApp +* Modules: MyMacros Utils Registry Data + +If the Basic library MyApp does not exist, it will be created. +Missing modules will be added, and excess modules will be removed. + +On the other hand, given the following Basic macros: + +* Library: MyApp +* Modules: MyMacros Utils Registry Data + +Running "obasync --get" will synchronize them with the following +source files: + +* Directory: MyApp +* Files: MyMacros.vb Utils.vb Registry.vb Data.vb + +Missing source files will be added, and excess source files will be +deleted. + +SYNOPSIS + + obasync [options] [DIRECTORY [LIBRARY]] + +DIRECTORY The project source directory. Default to the current + working directory. + +LIBRARY The name of the Basic library. Default to the same + name as the project source directory. + +--get Download (check out) the macros from the + OpenOffice/LibreOffice Basic storage to the source + files, instead of upload (check in). By default it + uploads the source files onto the + OpenOffice/LibreOffice Basic storage. + +-p, --port N The TCP port to communicate with + OpenOffice/LibreOffice. The default is 2002. You can + change it if port 2002 is already in use. + +-x, --ext .EXT The file name extension of the source files. The + default is ".vb". This may be used for your + convenience of editor syntax highlighting. + +-e, --encoding CS + The encoding of the source files. The default is + system-dependent. For example, on Traditional Chinese + MS-Windows, this will be CP950 (Big5). You can change + this to UTF-8 for convenience if you + obtain/synchronize your source code from other + sources. + +-r, --run MODULE.MACRO + Run he specific macro after synchronization, for + convenience. + +-h, --help Show the help message and exit + +-v, --version Show program’s version number and exit +""" + from __future__ import print_function import argparse import os @@ -12,8 +100,7 @@ import locale def append_uno_path(): - """ Appends the path of the uno module to the import path. """ - + """Append the path of the uno module to the import path.""" for p in sys.path: if os.path.exists(os.path.join(p, "uno.py")): return @@ -36,18 +123,49 @@ from com.sun.star.connection import NoConnectException def main(): - """ The main program. """ + """The main program.""" t_start = time.time() global args # Parses the arguments parse_args() - # Connects to the OpenOffice/LibreOffice - oo = Office(args.port) + # Downloads the macros from OpenOffice/LibreOffice Basic + if args.get: + oo = Office(args.port) + libraries = oo.service_manager.createInstance( + "com.sun.star.script.ApplicationScriptLibraryContainer") + modules = read_basic_modules(libraries, args.library) + if len(modules) == 0: + print("ERROR: Library %s does not exist" % args.library, + file=sys.stderr) + return + update_source_dir( + args.projdir, modules, args.ext, args.encoding) - # Synchronize the Basic macros. - sync_macros(oo, args.ext, args.encoding) + # Uploads the macros onto OpenOffice/LibreOffice Basic + else: + modules = read_in_source_dir( + args.projdir, args.ext, args.encoding) + if len(modules) == 0: + print("ERROR: Found no source macros in %s" % + args.projdir, + file=sys.stderr) + return + oo = Office(args.port) + libraries = oo.service_manager.createInstance( + "com.sun.star.script.ApplicationScriptLibraryContainer") + update_basic_modules(libraries, args.library, modules, oo) + if args.run is not None: + factory = oo.service_manager.DefaultContext.getByName( + "/singletons/com.sun.star.script.provider." + "theMasterScriptProviderFactory") + provider = factory.createScriptProvider("") + script = provider.getScript( + "vnd.sun.star.script:%s.%s" + "?language=Basic&location=application" % + (args.library, args.run)) + script.invoke((), (), ()) print("Done. %02d:%02d elapsed." % (int((time.time() - t_start) / 60), @@ -55,7 +173,7 @@ def main(): def parse_args(): - """ Parses the arguments. """ + """Parse the arguments.""" global args parser = argparse.ArgumentParser( @@ -89,7 +207,7 @@ def parse_args(): "-r", "--run", metavar="MODULE.MACRO", help="The macro to run after the upload, if any.") parser.add_argument( - "-v", "--version", action="version", version="%(prog)s 0.2") + "-v", "--version", action="version", version="%(prog)s 0.3") args = parser.parse_args() # Obtains the absolute path @@ -104,45 +222,20 @@ def parse_args(): return -def sync_macros(oo, ext, encoding): - """ Synchronize the Basic macros. """ - global args - - libraries = oo.service_manager.createInstance( - "com.sun.star.script.ApplicationScriptLibraryContainer") - # Downloads the macros from OpenOffice/LibreOffice Basic - if args.get: - modules = read_basic_modules(libraries, args.library) - if len(modules) == 0: - print("ERROR: Library %s does not exist" % args.library, - file=sys.stderr) - return - update_source_dir(args.projdir, modules, ext, encoding) - - # Uploads the macros onto OpenOffice/LibreOffice Basic - else: - modules = read_in_source_dir(args.projdir, ext, encoding) - if len(modules) == 0: - print("ERROR: Found no source macros in %s" % - args.projdir, - file=sys.stderr) - return - update_basic_modules(libraries, args.library, modules, oo) - if args.run is not None: - factory = oo.service_manager.DefaultContext.getByName( - "/singletons/com.sun.star.script.provider." - "theMasterScriptProviderFactory") - provider = factory.createScriptProvider("") - script = provider.getScript( - "vnd.sun.star.script:%s.%s" - "?language=Basic&location=application" % - (args.library, args.run)) - script.invoke((), (), ()) - return - - def read_in_source_dir(projdir, ext, encoding): - """ Reads-in the source macros. """ + """Read-in the source files. + + Arguments: + projdir: The project source directory. + ext: The file name extension of the source files, beginning + with a single dot ".". + encoding: The encoding of the source file. + + Returns: + The Basic modules that read, as a dictionary. The keys of the + dictionary are the module names, and the values of the + dictionary are the module contents. + """ modules = {} for entry in os.listdir(projdir): path = os.path.join(projdir, entry) @@ -154,7 +247,17 @@ def read_in_source_dir(projdir, ext, encoding): def update_source_dir(projdir, modules, ext, encoding): - """ Updates the source macros. """ + """Update the source files. + + Arguments: + projdir: The project source directory. + modules: The Basic modules, as a dictionary. The keys of + the dictionary are the module names, and the values of + the dictionary are the module contents. + ext: The file name extension of the source files, beginning + with a single dot ".". + encoding: The encoding of the source file. + """ curmods = {} is_in_sync = True for entry in os.listdir(projdir): @@ -187,8 +290,21 @@ def update_source_dir(projdir, modules, ext, encoding): def read_basic_modules(libraries, libname): - """ Reads the OpenOffice/LibreOffice Basic macros from the macros - storage. """ + """ + Read the OpenOffice/LibreOffice Basic macros from the macro + storage. + + Arguments: + libraries: The Basic library storage, as a + com.sun.star.script.ApplicationScriptLibraryContainer + service object. + libname: The name of the Basic library. + + Returns: + The Basic modules that read, as a dictionary. The keys of the + dictionary are the module names, and the values of the + dictionary are the module contents. + """ modules = {} if not libraries.hasByName(libname): return modules @@ -200,7 +316,19 @@ def read_basic_modules(libraries, libname): def update_basic_modules(libraries, libname, modules, oo): - """ Updates the OpenOffice/LibreOffice Basic macro storage. """ + """Update the OpenOffice/LibreOffice Basic macro storage. + + Arguments: + libraries: The Basic library storage, as a + com.sun.star.script.ApplicationScriptLibraryContainer + service object. + libname: The name of the Basic library. + modules: The Basic modules, as a dictionary. The keys of + the dictionary are the module names, and the values of + the dictionary are the module contents. + oo: The OpenOffice/LibreOffice connection, as an Office + object. + """ if not libraries.hasByName(libname): libraries.createLibrary(libname) print("Script library %s created." % libname, file=sys.stderr) @@ -232,7 +360,13 @@ def update_basic_modules(libraries, libname, modules, oo): def create_dialog_library(oo, libname): - """ Creates the dialog library. """ + """Create the dialog library. + + Arguments: + oo: The OpenOffice/LibreOffice connection, as an Office + object. + libname: The name of the dialog library. + """ libraries = oo.service_manager.createInstance( "com.sun.star.script.ApplicationDialogLibraryContainer") libraries.createLibrary(libname) @@ -241,7 +375,15 @@ def create_dialog_library(oo, libname): def read_file(path, encoding): - """ Reads a file, and deals with Python 3 / 2 compatibility. """ + """Read a file, and deals with Python 3 / 2 compatibility. + + Arguments: + path: The full path of the file. + encoding: The encoding of the file. + + Returns: + The content of the file. + """ if sys.version_info.major == 2: f = open(path) content = f.read().decode(encoding).replace("\r\n", "\n") @@ -254,8 +396,13 @@ def read_file(path, encoding): def write_file(path, content, encoding): - """ Writes to a file, and deals with Python 3 / 2 - compatibility. """ + """Write to a file, and deals with Python 3 / 2 compatibility. + + Arguments: + path: The full path of the file. + content: The content of the file. + encoding: The encoding of the file. + """ if sys.version_info.major == 2: f = open(path, "w") f.write(content.encode(encoding)) @@ -268,8 +415,19 @@ def write_file(path, content, encoding): def update_file(path, content, encoding): - """ Updates a file, and deals with Python 3 / 2 - compatibility. """ + """Update a file, and deals with Python 3 / 2 compatibility. + + The file will only be update if its content is different from + the supplied content. + + Arguments: + path: The full path of the file. + content: The new content of the file. + encoding: The encoding of the file. + + Returns: + True if the file is updated, or False otherwise. + """ is_updated = False if sys.version_info.major == 2: f = open(path, "r+") @@ -291,10 +449,30 @@ def update_file(path, content, encoding): class Office: - """ The OpenOffice/LibreOffice connection. """ + """The OpenOffice/LibreOffice connection. + + Attributes: + port: The TCP port that is used to communicate with the + OpenOffice/LibreOffice process. OpenOffice/LibreOffice + will listen to this port. + bootstrap_context: The bootstrap context of the + OpenOffice/LibreOffice desktop. + service_manager: The service manager of the + OpenOffice/LibreOffce desktop. + desktop: The OpenOffice/LibreOffice desktop object. + """ def __init__(self, port=2002): - """ Initializes the object.""" + """Initialize the OpenOffice/LibreOffice connection. + + Arguments: + port: The TCP port to communicate with + OpenOffice/LibreOffice with. OpenOffice/LibreOffice + will start to listen on this port if it is not + listening on this port not yet. The default is + port 2002. Change it to another port if port 2002 is + already in use. + """ self.port = port self.bootstrap_context = None self.service_manager = None @@ -302,7 +480,11 @@ class Office: self.connect() def connect(self): - """ Connects to the running OpenOffice/LibreOffice process.""" + """Connect to the running OpenOffice/LibreOffice process. + + Run OpenOffice/LibreOffice in server listening mode if it is + not running yet. + """ # Obtains the local context local_context = uno.getComponentContext() # Obtains the local service manager @@ -327,15 +509,14 @@ class Office: "com.sun.star.frame.Desktop", self.bootstrap_context) def start_oo(self): - """ Starts the OpenOffice/LibreOffice in server listening - mode. """ + """Start OpenOffice/LibreOffice in server listening mode.""" # For MS-Windows, which does not have fork() if os.name == "nt": from subprocess import Popen - ooexec = os.path.join( + soffice = os.path.join( os.path.dirname(uno.__file__), "soffice.exe") DETACHED_PROCESS = 0x00000008 - Popen([ooexec, + Popen([soffice, "-accept=socket,host=localhost,port=%d;urp;" % self.port], close_fds=True, creationflags=DETACHED_PROCESS) @@ -352,8 +533,8 @@ class Office: time.sleep(2) return os.setsid() - ooexec = self.find_posix_ooexec() - if ooexec is None: + soffice = self.find_posix_soffice() + if soffice is None: print("Failed to find the " "OpenOffice/LibreOffice installation.", file=sys.stderr) @@ -362,56 +543,64 @@ class Office: self.port # LibreOffice on POSIX systems uses --accept instead of # -accept now. - if self.is_ooexec_lo(ooexec): + if self.is_soffice_lo(soffice): param = "-" + param try: - os.execl(ooexec, ooexec, param) + os.execl(soffice, soffice, param) except OSError: print("%s: Failed to run the" - " OpenOffice/LibreOffice server." % ooexec, + " OpenOffice/LibreOffice server." % soffice, file=sys.stderr) sys.exit(1) - def find_posix_ooexec(self): - """ Finds the OpenOffice/LibreOffice executable on POSIX - systems (Linux or MacOSX). """ + def find_posix_soffice(self): + """Find soffice on POSIX systems (Linux or MacOSX). + + Returns: + The found soffice executable, or None if not found. + """ # Checkes soffice in the same directory of uno.py # This works for Linux OpenOffice/LibreOffice local # installation, and OpenOffice on MacOSX. - ooexec = os.path.join( + soffice = os.path.join( os.path.dirname(uno.__file__), "soffice") - if os.path.exists(ooexec): - return ooexec - + if os.path.exists(soffice): + return soffice + # Now we have LibreOffice on MacOSX and Linux # OpenOffice/LibreOffice vender installation. - + # LibreOffice on MacOSX. - ooexec = "/Applications/LibreOffice.app/Contents/MacOS/soffice" - if os.path.exists(ooexec): - return ooexec - + soffice = "/Applications/LibreOffice.app/Contents/MacOS/soffice" + if os.path.exists(soffice): + return soffice + # Linux OpenOffice/LibreOffice vender installation. - ooexec = "/usr/bin/soffice" - if os.path.exists(ooexec): - return ooexec - + soffice = "/usr/bin/soffice" + if os.path.exists(soffice): + return soffice + # Not found return None - def is_ooexec_lo(self, ooexec): - """ Checks whether the soffice executable is LibreOffice. - LibreOffice on POSIX systems uses --accept instead of - -accept now. """ + def is_soffice_lo(self, soffice): + """Check whether the soffice executable is LibreOffice. + + LibreOffice on POSIX systems accepts "--accept" instead of + "-accept" now. + + Returns: + True if soffice is LibreOffice, or False otherwise. + """ # This works for most cases. - if ooexec.lower().find("libreoffice") != -1: + if soffice.lower().find("libreoffice") != -1: return True - + # Checks the symbolic link at /usr/bin/soffice - if ooexec == "/usr/bin/soffice" and os.path.islink(ooexec): - if os.readlink(ooexec).lower().find("libreoffice") != -1: + if soffice == "/usr/bin/soffice" and os.path.islink(soffice): + if os.readlink(soffice).lower().find("libreoffice") != -1: return True - + # Not found return False diff --git a/setup.py b/setup.py index 6c0e97f..d77051c 100755 --- a/setup.py +++ b/setup.py @@ -1,18 +1,35 @@ +#! /opt/openoffice4/program/python +# -*- coding: utf-8 -*- +# Python setuptools installer for the obasync project. +# by imacat , 2016-12-20 + +# Copyright (c) 2016 imacat. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import os import sys from setuptools import setup -# For Python shipped with OpenOffice, python is a wrapper -# for python.bin that fixes the import and library path. We should -# call python instead of python.bin. """ -import os -import sys +# For Python shipped with OpenOffice, "python" is a shell script +# wrapper for the real executable "python.bin" that sets the import +# and library path. We should call "python" instead of "python.bin". if os.path.basename(sys.executable) == "python.bin": sys.executable = os.path.join( os.path.dirname(sys.executable), "python") setup(name="obasync", - version="0.2", + version="0.3", description="Office Basic macro source synchronizer", url="https://github.com/imacat/obasync", author="imacat",