Skip to content

Commit

Permalink
Move raw bindings to separate module (CC #256)
Browse files Browse the repository at this point in the history
Enable the caller to select the modules to install using the env var
`$PYPDFIUM_MODULES`. This is an important pre-requisite for conda
packaging.

That said, it is just cleaner to isolate the raw bindings in a separate
module, so callers may use that without any automatic init from helper.
It even allows not to install helpers at all if you don't use them.
Similarly, it would allow to install only the helpers and plug in a
pypdfium2_raw supplied otherwise.

However, note that version integration is now really polluted (1 version
file for two separate modules - threatens desync).
In the future, we want to split this in two separate version files and
probably also use a yaml delegate for r/w instead of working directly
with the python file.
  • Loading branch information
mara004 committed Sep 30, 2023
1 parent 89aaf06 commit 36701a6
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 39 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ docs/build/
sourcebuild/
!sourcebuild/patches

src/pypdfium2/raw.py
src/pypdfium2/pdfium
src/pypdfium2_raw/bindings.py

*.so
*.dll
Expand Down
2 changes: 1 addition & 1 deletion .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Source: https://github.com/pypdfium2-team/pypdfium2

Files:
req/*.txt
bindings/raw.py
bindings/bindings.py
autorelease/*
RELEASE.md
tests/expectations/*
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Nonetheless, the following guide may be helpful to get started with the raw API,
Python `bytes` are converted to `FPDF_STRING` by ctypes autoconversion.
When passing a string to a C function, it must always be null-terminated, as the function merely receives a pointer to the first item and then continues to read memory until it finds a null terminator.
[^bindings_decl]: From the auto-generated bindings file, which is not part of the repository. It is built into wheels, or created on installation. If you have an editable install, the bindings file may be found at `src/raw.py`.
[^bindings_decl]: From the auto-generated bindings file. We maintain a reference copy at `bindings/bindings.py`. Or if you have an editable install, there will also be `src/pypdfium2_raw/bindings.py`.
* While some functions are quite easy to use, things soon get more complex.
First of all, function parameters are not only used for input, but also for output:
Expand Down
2 changes: 1 addition & 1 deletion bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[#192]: https://github.com/pypdfium2-team/pypdfium2/issues/192

This directory contains a reference bindings file for pypdfium2 ([raw.py](./raw.py)).
This directory contains a reference bindings file for pypdfium2 ([bindings.py](./bindings.py)).
It is updated automatically on release.

This helps track changes to the bindings. See [#192] for an extended rationale.
File renamed without changes.
6 changes: 1 addition & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ requires = [
name = "pypdfium2"
description = "Python bindings to PDFium"
readme = "README.md"
requires-python = ">= 3.6"
dynamic = ["version"]
dynamic = ["version", "requires-python"]
keywords = ["pdf", "pdfium"]
authors = [
{name = "pypdfium2-team"},
Expand Down Expand Up @@ -58,6 +57,3 @@ license-files = [
"LICENSES/LicenseRef-PdfiumThirdParty.txt",
".reuse/dep5",
]

[tool.setuptools.packages.find]
where = ["src"]
4 changes: 2 additions & 2 deletions run
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function check() {
}

function clean() {
rm -rf src/pypdfium2.egg-info/ dist data/*
rm -rf src/pypdfium2.egg-info/ dist/ data/*
rm -f tests/output/* tests_old/output/* src/pypdfium2.egg-info/SOURCES.txt
}

Expand All @@ -39,7 +39,7 @@ test)
python3 -m pytest tests/ tests_old/ $args;;

coverage)
python3 -m coverage run --omit "tests/*,tests_old/*,src/pypdfium2/raw.py,setupsrc/*" -m pytest tests/ tests_old/ $args
python3 -m coverage run --omit "tests/*,tests_old/*,src/pypdfium2_raw/bindings.py,setupsrc/*" -m pytest tests/ tests_old/ $args
python3 -m coverage report;;

docs-build)
Expand Down
14 changes: 10 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,30 @@
sys.path.insert(0, str(Path(__file__).parent / "setupsrc"))
from pypdfium2_setup.emplace import get_pdfium
from pypdfium2_setup.packaging_base import (
# TODO consider glob import or dotted access?
purge_pdfium_versions,
BinarySpec_EnvVar,
PlatformTarget_None,
ModulesSpec_EnvVar,
)


def main():

from pypdfium2_setup.setup_base import mkwheel, SetupKws
from pypdfium2_setup.setup_base import mkwheel, get_setup_kws

binary_spec = os.environ.get(BinarySpec_EnvVar, "")
modules_spec = os.environ.get(ModulesSpec_EnvVar, "")
if modules_spec:
modules_spec = modules_spec.split(",")

binary_spec = os.environ.get(BinarySpec_EnvVar, "").strip()
if binary_spec == PlatformTarget_None:
purge_pdfium_versions()
setuptools.setup(**SetupKws)
setuptools.setup(**get_setup_kws(modules_spec))
return

pl_name = get_pdfium(binary_spec)
mkwheel(pl_name)
mkwheel(pl_name, modules_spec)


if __name__ == "__main__":
Expand Down
4 changes: 2 additions & 2 deletions setupsrc/pypdfium2_setup/craft_packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self):

# FIXME some degree of duplication with base::get_platfiles()
file_names = [BindingsFileName, LibnameForSystem[Host.system]]
self.files = [fp for fp in [ModuleDir / fn for fn in file_names] if fp.exists()]
self.files = [fp for fp in [RawModuleDir / fn for fn in file_names] if fp.exists()]
if len(self.files) == 0:
return
elif len(self.files) != 2:
Expand All @@ -38,7 +38,7 @@ def pop(self):
if self.tmpdir is None:
return
for fp in self.files:
shutil.move(self.tmpdir_path / fp.name, ModuleDir)
shutil.move(self.tmpdir_path / fp.name, RawModuleDir)
self.tmpdir.cleanup()


Expand Down
1 change: 0 additions & 1 deletion setupsrc/pypdfium2_setup/emplace.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def main():
)
parser.add_argument(
"binary_spec",
type = str.strip,
default = os.environ.get(BinarySpec_EnvVar, ""),
nargs = "?",
help = f"The binary specifier. Same format as of ${BinarySpec_EnvVar} on setup.",
Expand Down
18 changes: 12 additions & 6 deletions setupsrc/pypdfium2_setup/packaging_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@
VerStatusFileName = ".pdfium_version.txt"
V8StatusFileName = ".pdfium_is_v8.txt"
# NOTE if renaming BindingsFileName, also rename `bindings/$FILE`
BindingsFileName = "raw.py"
BindingsFileName = "bindings.py"

HomeDir = Path.home()
SourceTree = Path(__file__).parents[2]
DataTree = SourceTree / "data"
SB_Dir = SourceTree / "sourcebuild"
ModuleDir = SourceTree / "src" / "pypdfium2"
VersionFile = ModuleDir / "version.py"
RawModuleDir = SourceTree / "src" / "pypdfium2_raw"
HelpersModuleDir = SourceTree / "src" / "pypdfium2"
VersionFile = HelpersModuleDir / "version.py"
Changelog = SourceTree / "docs" / "devel" / "changelog.md"
ChangelogStaging = SourceTree / "docs" / "devel" / "changelog_staging.md"
AutoreleaseDir = SourceTree / "autorelease"
Expand All @@ -40,6 +41,11 @@
PlatformTarget_Auto = "auto" # host
VersionTarget_Latest = "latest"

ModulesSpec_EnvVar = "PYPDFIUM_MODULES"
ModuleRaw = "raw"
ModuleHelpers = "helpers"
ModulesAll = [ModuleRaw, ModuleHelpers]

RepositoryURL = "https://github.com/pypdfium2-team/pypdfium2"
PDFium_URL = "https://pdfium.googlesource.com/pdfium"
DepotTools_URL = "https://chromium.googlesource.com/chromium/tools/depot_tools.git"
Expand Down Expand Up @@ -325,9 +331,9 @@ def clean_platfiles():

deletables = [
SourceTree / "build",
ModuleDir / BindingsFileName,
RawModuleDir / BindingsFileName,
]
deletables += [ModuleDir / fn for fn in MainLibnames]
deletables += [RawModuleDir / fn for fn in MainLibnames]

for fp in deletables:
if fp.is_file():
Expand Down Expand Up @@ -367,7 +373,7 @@ def emplace_platfiles(pl_name):
for fp in platfiles:
if not fp.exists():
raise RuntimeError(f"Platform file missing: {fp}")
shutil.copy(fp, ModuleDir)
shutil.copy(fp, RawModuleDir)


def get_changelog_staging(flush=False):
Expand Down
34 changes: 22 additions & 12 deletions setupsrc/pypdfium2_setup/setup_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,7 @@

sys.path.insert(0, str(Path(__file__).parents[1]))
# TODO consider glob import or dotted access
from pypdfium2_setup.packaging_base import (
VerNamespace,
LibnameForSystem,
plat_to_system,
get_wheel_tag,
emplace_platfiles,
)
from pypdfium2_setup.packaging_base import *


def bdist_factory(pl_name):
Expand All @@ -36,12 +30,28 @@ def has_ext_modules(self):
return True


SetupKws = dict(
version = VerNamespace["V_PYPDFIUM2"],
)
def get_setup_kws(modnames=None):

if not modnames:
modnames = ModulesAll

module_dirs = {}
if ModuleRaw in modnames:
module_dirs["pypdfium2_raw"] = "src/pypdfium2_raw"
if ModuleHelpers in modnames:
module_dirs["pypdfium2"] = "src/pypdfium2"
assert len(module_dirs) in (1, 2)
assert len(modnames) == len(module_dirs)

return dict(
version = VerNamespace["V_PYPDFIUM2"],
package_dir = module_dirs,
# NOTE if necessary, python req could be set dynamically depending on included modules
python_requires = ">= 3.6",
)


def mkwheel(pl_name):
def mkwheel(pl_name, modnames=None):

emplace_platfiles(pl_name)
system = plat_to_system(pl_name)
Expand All @@ -51,5 +61,5 @@ def mkwheel(pl_name):
package_data = {"": [libname]},
cmdclass = {"bdist_wheel": bdist_factory(pl_name)},
distclass = BinaryDistribution,
**SetupKws,
**get_setup_kws(modnames),
)
4 changes: 4 additions & 0 deletions src/pypdfium2/raw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2023 geisserml <[email protected]>
# SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause

from pypdfium2_raw.bindings import *
4 changes: 4 additions & 0 deletions src/pypdfium2_raw/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: 2023 geisserml <[email protected]>
# SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause

from pypdfium2_raw.bindings import *
4 changes: 2 additions & 2 deletions tests_old/test_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ def test_paths():
assert pkg_base.SourceTree == SourceTree
assert pkg_base.DataTree == SourceTree / "data"
assert pkg_base.SB_Dir == SourceTree / "sourcebuild"
assert pkg_base.ModuleDir == SourceTree / "src" / "pypdfium2"
assert pkg_base.VersionFile == Path(pkg_base.ModuleDir) / "version.py"
assert pkg_base.HelpersModuleDir == SourceTree / "src" / "pypdfium2"
assert pkg_base.VersionFile == Path(pkg_base.HelpersModuleDir) / "version.py"


# update_pdfium
Expand Down

0 comments on commit 36701a6

Please sign in to comment.