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.

This commit is contained in:
依瑪貓 2017-01-05 16:30:36 +08:00
parent 5ae41d2fc5
commit 0425368b7d
3 changed files with 468 additions and 124 deletions

View File

@ -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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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
<https://pypi.python.org/pypi/obasync>`_ or `GitHub
<https://github.com/imacat/obasync>`_. 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 programs version number and exit
COPYRIGHT

View File

@ -3,6 +3,94 @@
# Office Basic macro source synchronizer.
# by imacat <imacat@mail.imacat.idv.tw>, 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 programs 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

View File

@ -1,18 +1,35 @@
#! /opt/openoffice4/program/python
# -*- coding: utf-8 -*-
# Python setuptools installer for the obasync project.
# by imacat <imacat@mail.imacat.idv.tw>, 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",