diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 993c8aea..1ba4b504 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,8 +6,7 @@ on: - v* env: - LIBZIM_RELEASE: libzim_linux-x86_64-6.1.1 - LIBZIM_LIBRARY_PATH: lib/x86_64-linux-gnu/libzim.so.6.1.1 + LIBZIM_RELEASE: libzim_linux-x86_64-6.1.5 LIBZIM_INCLUDE_PATH: include/zim CYTHON_VERSION: 0.29.6 @@ -46,22 +45,25 @@ jobs: - name: Link libzim dylib & headers into workspace lib and include folders run: | - cp -p $GITHUB_WORKSPACE/libzim_linux/$LIBZIM_LIBRARY_PATH lib/libzim.so - cp -p $GITHUB_WORKSPACE/libzim_linux/$LIBZIM_LIBRARY_PATH lib/ - sudo ldconfig $GITHUB_WORKSPACE/lib + cp -dp $GITHUB_WORKSPACE/libzim_linux/lib/x86_64-linux-gnu/* lib/ + ln -s libzim.so.6 lib/libzim.so ln -s $GITHUB_WORKSPACE/libzim_linux/$LIBZIM_INCLUDE_PATH include/zim - name: Build cython, sdist, and bdist_wheels run: | - pip install --upgrade cython==$CYTHON_VERSION setuptools pip - python3 setup.py build_ext - python3 setup.py sdist bdist_wheel - python -m cibuildwheel --output-dir wheelhouse + pip install --upgrade cython==$CYTHON_VERSION setuptools pip wheel + python3 setup.py build_ext --rpath='$ORIGIN' + if [[ "${{ matrix.python-version }}" == "3.6" ]] + then + python3 setup.py sdist + fi + cp -p lib/libzim.so.6 libzim + python3 setup.py bdist_wheel --plat-name=manylinux1_x86_64 - uses: actions/upload-artifact@v1 with: name: wheels - path: ./wheelhouse + path: ./dist - name: Push release to PyPI if: github.event_name == 'push' && github.ref == 'refs/heads/master' @@ -69,5 +71,3 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} - # TODO: remove this line to upload to the real PyPI when ready - repository_url: https://test.pypi.org/legacy/ diff --git a/.gitignore b/.gitignore index c1568563..cc8391b1 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ include/* libzim_linux-*/ # Autogenerated files -libzim_wrapper.*.so -libzim/libzim_wrapper.cpp -libzim/libzim_wrapper.h -libzim/libzim_wrapper_api.h +wrapper.*.so +libzim/wrapper.cpp +libzim/wrapper.h +libzim/wrapper_api.h diff --git a/MANIFEST.in b/MANIFEST.in index 066d1e08..f5364038 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,6 @@ include README.md include tests/*.py include pyproject.toml -recursive-include lib * -recursive-include include * recursive-include libzim * +global-exclude __pycache__/* +exclude *.egg-info/* diff --git a/libzim/lib.cxx b/libzim/lib.cxx index 011691de..429c342a 100644 --- a/libzim/lib.cxx +++ b/libzim/lib.cxx @@ -23,7 +23,7 @@ #include #include "lib.h" -#include "libzim_wrapper_api.h" +#include "wrapper_api.h" #include #include @@ -38,7 +38,7 @@ ZimArticleWrapper::ZimArticleWrapper(PyObject *obj) : m_obj(obj) { - if (import_libzim_wrapper()) + if (import_libzim__wrapper()) { std::cerr << "Error executing import_libzim!\n"; throw std::runtime_error("Error executing import_libzim"); diff --git a/libzim/reader.py b/libzim/reader.py index 645dcb92..bef2f925 100644 --- a/libzim/reader.py +++ b/libzim/reader.py @@ -1,4 +1,4 @@ # flake8: noqa -from libzim_wrapper import File -from libzim_wrapper import ReadArticle as Article +from .wrapper import FilePy as File +from .wrapper import ReadArticle as Article diff --git a/libzim/libzim_wrapper.pxd b/libzim/wrapper.pxd similarity index 100% rename from libzim/libzim_wrapper.pxd rename to libzim/wrapper.pxd diff --git a/libzim/libzim_wrapper.pyx b/libzim/wrapper.pyx similarity index 91% rename from libzim/libzim_wrapper.pyx rename to libzim/wrapper.pyx index baf98bc1..3b0627e6 100644 --- a/libzim/libzim_wrapper.pyx +++ b/libzim/wrapper.pyx @@ -18,7 +18,7 @@ # along with this program. If not, see . -cimport libzim.libzim_wrapper as clibzim +cimport libzim.wrapper as wrapper from cython.operator import dereference, preincrement from cpython.ref cimport PyObject @@ -36,7 +36,7 @@ import datetime ######################### cdef class WritingBlob: - cdef clibzim.Blob* c_blob + cdef wrapper.Blob* c_blob cdef bytes ref_content def __cinit__(self, content): @@ -44,7 +44,7 @@ cdef class WritingBlob: self.ref_content = content.encode('UTF-8') else: self.ref_content = content - self.c_blob = new clibzim.Blob( self.ref_content, len(self.ref_content)) + self.c_blob = new wrapper.Blob( self.ref_content, len(self.ref_content)) def __dealloc__(self): if self.c_blob != NULL: @@ -53,11 +53,11 @@ cdef class WritingBlob: cdef Py_ssize_t itemsize = 1 cdef class ReadingBlob: - cdef clibzim.Blob c_blob + cdef wrapper.Blob c_blob cdef Py_ssize_t size cdef int view_count - cdef __setup(self, clibzim.Blob blob): + cdef __setup(self, wrapper.Blob blob): """Assigns an internal pointer to the wrapped C++ article object. Parameters @@ -116,7 +116,7 @@ cdef public api: ret_str = func() return ret_str.encode('UTF-8') - clibzim.Blob blob_cy_call_fct(object obj, string method, int *error) with gil: + wrapper.Blob blob_cy_call_fct(object obj, string method, int *error) with gil: """Lookup and execute a pure virtual method on ZimArticle returning a Blob""" cdef WritingBlob blob @@ -146,7 +146,7 @@ cdef class Creator: flag if the creator was finalized """ - cdef clibzim.ZimCreatorWrapper *c_creator + cdef wrapper.ZimCreatorWrapper *c_creator cdef bool _finalized def __cinit__(self, str filename, str main_page = "", str index_language = "eng", min_chunk_size = 2048): @@ -163,7 +163,7 @@ cdef class Creator: Minimum chunk size (default 2048) """ - self.c_creator = clibzim.ZimCreatorWrapper.create(filename.encode("UTF-8"), main_page.encode("UTF-8"), index_language.encode("UTF-8"), min_chunk_size) + self.c_creator = wrapper.ZimCreatorWrapper.create(filename.encode("UTF-8"), main_page.encode("UTF-8"), index_language.encode("UTF-8"), min_chunk_size) self._finalized = False def __dealloc__(self): @@ -185,8 +185,8 @@ cdef class Creator: raise RuntimeError("ZimCreator already finalized") # Make a shared pointer to ZimArticleWrapper from the ZimArticle object - cdef shared_ptr[clibzim.ZimArticleWrapper] art = shared_ptr[clibzim.ZimArticleWrapper]( - new clibzim.ZimArticleWrapper(article)); + cdef shared_ptr[wrapper.ZimArticleWrapper] art = shared_ptr[wrapper.ZimArticleWrapper]( + new wrapper.ZimArticleWrapper(article)); with nogil: self.c_creator.addArticle(art) @@ -239,7 +239,7 @@ cdef class ReadArticle: from_read_article(zim.Article art) Creates a python ZimArticle from a C++ zim.Article article. """ - cdef clibzim.Article c_article + cdef wrapper.Article c_article cdef ReadingBlob _blob cdef bool _haveBlob @@ -251,7 +251,7 @@ cdef class ReadArticle: def __cinit__(self): self._haveBlob = False - cdef __setup(self, clibzim.Article art): + cdef __setup(self, wrapper.Article art): """Assigns an internal pointer to the wrapped C++ article object. Parameters @@ -267,7 +267,7 @@ cdef class ReadArticle: # Factory functions - Currently Cython can't use classmethods @staticmethod - cdef from_read_article(clibzim.Article art): + cdef from_read_article(wrapper.Article art): """Creates a python ZimFileArticle from a C++ Article (zim::). Parameters @@ -332,7 +332,7 @@ cdef class ReadArticle: # File # ######################### -cdef class File: +cdef class FilePy: """ A class to represent a Zim File Reader. @@ -344,7 +344,7 @@ cdef class File: the file name of the File Reader object """ - cdef clibzim.File *c_file + cdef wrapper.File *c_file cdef object _filename def __cinit__(self, str filename): @@ -355,7 +355,7 @@ cdef class File: Full path to a zim file """ - self.c_file = new clibzim.File(filename.encode('UTF-8')) + self.c_file = new wrapper.File(filename.encode('UTF-8')) self._filename = self.c_file.getFilename().decode("UTF-8", "strict") def __dealloc__(self): @@ -384,7 +384,7 @@ cdef class File: If an article with the provided long url is not found in the file """ # Read to a zim::Article - cdef clibzim.Article art = self.c_file.getArticleByUrl(url.encode('UTF-8')) + cdef wrapper.Article art = self.c_file.getArticleByUrl(url.encode('UTF-8')) if not art.good(): raise RuntimeError("Article not found for url") @@ -419,7 +419,7 @@ cdef class File: """ # Read to a zim::Article - cdef clibzim.Article art = self.c_file.getArticle( id) + cdef wrapper.Article art = self.c_file.getArticle( id) if not art.good(): raise RuntimeError("Article not found for id") @@ -435,8 +435,8 @@ cdef class File: The url of the main page TODO Check old formats """ - cdef clibzim.Fileheader header = self.c_file.getFileheader() - cdef clibzim.Article article + cdef wrapper.Fileheader header = self.c_file.getFileheader() + cdef wrapper.Article article if header.hasMainPage(): article = self.c_file.getArticle(header.getMainPage()) return article.getLongUrl().decode("UTF-8", "strict"); @@ -504,8 +504,8 @@ cdef class File: iterator An interator with the urls of suggested articles starting from start position """ - cdef unique_ptr[clibzim.Search] search = self.c_file.suggestions(query.encode('UTF-8'),start, end) - cdef clibzim.search_iterator it = dereference(search).begin() + cdef unique_ptr[wrapper.Search] search = self.c_file.suggestions(query.encode('UTF-8'),start, end) + cdef wrapper.search_iterator it = dereference(search).begin() while it != dereference(search).end(): yield it.get_url().decode('UTF-8') @@ -527,8 +527,8 @@ cdef class File: An iterator with the urls of articles matching the search query starting from start position """ - cdef unique_ptr[clibzim.Search] search = self.c_file.search(query.encode('UTF-8'),start, end) - cdef clibzim.search_iterator it = dereference(search).begin() + cdef unique_ptr[wrapper.Search] search = self.c_file.search(query.encode('UTF-8'),start, end) + cdef wrapper.search_iterator it = dereference(search).begin() while it != dereference(search).end(): yield it.get_url().decode('UTF-8') @@ -545,7 +545,7 @@ cdef class File: int Number of search results """ - cdef unique_ptr[clibzim.Search] search = self.c_file.search(query.encode('UTF-8'),0, 1) + cdef unique_ptr[wrapper.Search] search = self.c_file.search(query.encode('UTF-8'),0, 1) return dereference(search).get_matches_estimated() def get_suggestions_results_count(self, query): @@ -559,7 +559,7 @@ cdef class File: int Number of article suggestions """ - cdef unique_ptr[clibzim.Search] search = self.c_file.suggestions(query.encode('UTF-8'),0 , 1) + cdef unique_ptr[wrapper.Search] search = self.c_file.suggestions(query.encode('UTF-8'),0 , 1) return dereference(search).get_matches_estimated() def __repr__(self): diff --git a/libzim/writer.py b/libzim/writer.py index cd17f218..fedab2fb 100644 --- a/libzim/writer.py +++ b/libzim/writer.py @@ -21,8 +21,8 @@ import datetime from collections import defaultdict -import libzim_wrapper -from libzim_wrapper import WritingBlob as Blob +from .wrapper import Creator as _Creator +from .wrapper import WritingBlob as Blob __all__ = ["Article", "Blob", "Creator"] @@ -137,7 +137,7 @@ class Creator: def __init__(self, filename, main_page, index_language, min_chunk_size): print(filename) - self._creatorWrapper = libzim_wrapper.Creator(filename, main_page, index_language, min_chunk_size) + self._creatorWrapper = _Creator(filename, main_page, index_language, min_chunk_size) self.filename = filename self.main_page = main_page self.language = index_language diff --git a/setup.py b/setup.py index 736e3054..2405d0a9 100755 --- a/setup.py +++ b/setup.py @@ -34,17 +34,8 @@ from setuptools import setup, Extension from Cython.Build import cythonize - -PACKAGE_NAME = "libzim_wrapper" -VERSION = "0.0.1" # pegged to be the same version as libzim since they are always released together -LICENSE = "GPLv3+" -DESCRIPTION = "A python-facing API for creating and interacting with ZIM files" -AUTHOR = "Monadical Inc." -AUTHOR_EMAIL = "jdc@monadical.com" GITHUB_URL = "https://github.com/openzim/python-libzim" - BASE_DIR = Path(__file__).parent -BINDINGS_CYTHON_DIR = 'libzim' # the cython binding source dir (containing .pyx, .pyd, etc.) LIBZIM_INCLUDE_DIR = 'include' # the libzim C++ header src dir (containing zim/*.h) LIBZIM_LIBRARY_DIR = 'lib' # the libzim .so binary lib dir (containing libzim.so) @@ -55,7 +46,7 @@ f"[!] Warning: Couldn't find zim/*.h in ./{LIBZIM_INCLUDE_DIR}!\n" f" Hint: You can install them from source from https://github.com/openzim/libzim\n" f" or download a prebuilt release's headers into ./include/zim/*.h\n" - f" (or set CFLAGS='-I/tmp/libzim_linux-x86_64-{VERSION}/include')" + f" (or set CFLAGS='-I/include')" ) # Check for the CPP Libzim shared library in expected directory or system paths @@ -64,12 +55,27 @@ f"[!] Warning: Couldn't find libzim.so in ./{LIBZIM_LIBRARY_DIR} or system library paths!" f" Hint: You can install it from source from https://github.com/openzim/libzim\n" f" or download a prebuilt zimlib.so release into ./lib.\n" - f" (or set LDFLAGS='-L/tmp/libzim_linux-x86_64-{VERSION}/lib/x86_64-linux-gnu')" + f" (or set LDFLAGS='-L/lib/x86_64-linux-gnu')" ) +def get_long_description(): + return (BASE_DIR/'README.md').read_text() + +wrapper_extension = Extension( + name = "libzim.wrapper", + sources = ["libzim/wrapper.pyx", "libzim/lib.cxx"], + include_dirs=["libzim", LIBZIM_INCLUDE_DIR], + libraries=['zim'], + library_dirs=[LIBZIM_LIBRARY_DIR], + extra_compile_args=["-std=c++11", "-Wall", "-Wextra"], + language="c++", +) + + setup( - name=PACKAGE_NAME, - version=VERSION, +# Basic information about libzim module + name="libzim", + version="0.0.1", url=GITHUB_URL, project_urls={ 'Source': GITHUB_URL, @@ -78,43 +84,25 @@ 'Documentation': f'{GITHUB_URL}/blob/master/README.md', 'Donate': 'https://www.kiwix.org/en/support-us/', }, - author=AUTHOR, - author_email=AUTHOR_EMAIL, - license=LICENSE, - description=DESCRIPTION, - long_description=(BASE_DIR / 'README.md').read_text(), + author="Monadical Inc.", + author_email="jdc@monadical.com", + license="GPLv3+", + description="A python-facing API for creating and interacting with ZIM files", + long_description=get_long_description(), long_description_content_type="text/markdown", python_requires='>=3.6', - include_package_data=True, - ext_modules=cythonize( - [ - Extension( - "libzim_wrapper", - sources=[ - f"{BINDINGS_CYTHON_DIR}/*.pyx", - f"{BINDINGS_CYTHON_DIR}/lib.cxx", - ], - include_dirs=[ - BINDINGS_CYTHON_DIR, - LIBZIM_INCLUDE_DIR, - ], - libraries=[ - 'zim', - ], - library_dirs=[ - LIBZIM_LIBRARY_DIR, - ], - extra_compile_args=[ - "-std=c++11", - "-Wall", - "-Wextra", - ], - language="c++", - ) - ], - compiler_directives={"language_level" : "3"}, + + # Content + packages=["libzim"], + ext_modules=cythonize([wrapper_extension], + compiler_directives={"language_level": "3"} ), + +# Packaging + include_package_data=True, zip_safe=False, + +# Extra classifiers=[ "Development Status :: 3 - Alpha", @@ -134,7 +122,7 @@ "Intended Audience :: End Users/Desktop", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", - + "Programming Language :: Cython", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6",