python-perspective
maintainer thrasibule
· 0 votes
· scanned 2026-08-03 00:08:14.047287
LOW
View on AUR ↗
Why flagged
The package builds from source and only vendors prebuilt JupyterLab extension assets from PyPI, which are non-executable data; the core binary is compiled locally, and the source is from a trusted upstream repository.
Triggered rules
LOW
Few votes, recently uploaded
zero_votes_recent
Uploaded within the last 14 days with 2 or fewer community votes — little peer review so far.
LOW
AI review
llm_review
An AI model (qwen/qwen3-235b-a22b-2507) reviewed this and agrees it is LOW (confidence 95%): The package builds from source and only vendors prebuilt JupyterLab extension assets from PyPI, which are non-executable data; the core binary is compiled locally, and the source is from a trusted upstream repository.
PKGBUILD
1
# Maintainer: Guillaume Horel <guillaume.horel@gmail.com>
2
3
pkgname=python-perspective
4
_pkgname=perspective
5
pkgver=4.5.2
6
pkgrel=1
7
pkgdesc="Python bindings for Perspective, a high performance data-visualization and analytics component"
8
url="https://github.com/perspective-dev/perspective"
9
license=('Apache-2.0')
10
arch=('x86_64')
11
depends=('python' 'gcc-libs' 'glibc' 'abseil-cpp' 'protobuf')
12
makedepends=('cargo' 'cmake' 'maturin' 'python-build' 'python-installer' 'git' 'protobuf')
13
# The jupyterlab labextension's JS bundle requires nightly Rust to compile
14
# `perspective-viewer` to wasm (it uses iter_intersperse / try_blocks /
15
# more_qualified_paths). Rather than build a nightly toolchain, vendor the
16
# prebuilt labextension bytes from the official PyPI wheel (same upstream
17
# version we build the rest from) and graft them onto our wheel via maturin's
18
# `data = ...` directive. Our .so is still built from source.
19
_upstream_whl=perspective_python-${pkgver}-cp311-abi3-manylinux_2_28_x86_64.whl
20
source=("https://github.com/perspective-dev/perspective/archive/refs/tags/v$pkgver.tar.gz"
21
"https://files.pythonhosted.org/packages/89/94/94c06faff7430c8b5c48bed41da759e1e4894528b2e2373617adf9d5f1e4/${_upstream_whl}")
22
sha512sums=('46fc86ac07a4457f79e0a571d939540678d8bfce8bdce19bbcaceb5d7861c15aa153f71774211d426e2ad5748b7d55482aa9e91500e5cf0daa32ace51f43251e'
23
'36500ccf7eadb33098ccda45205b829dbadc8352266eb5a44c4ac5c60bd50dfedebfe810f46be59da18a2374c59c1fa7924435700c75531b706e4f61bf51faa1')
24
25
prepare() {
26
cd "$_pkgname-$pkgver"
27
# Drop workspace members that require nightly Cargo (`-Z bindeps`)
28
sed -i '/"rust\/lint",/d;/"rust\/metadata",/d;/"rust\/bundle",/d;/"examples\/rust-axum",/d' Cargo.toml
29
30
# Expose perspective-client's `omit_metadata` feature through perspective-python
31
# so we can skip pulling in `docs/expression_gen.md` (generated by the metadata
32
# crate, which we just removed from the workspace).
33
sed -i '/^default = \[\]$/a omit_metadata = ["perspective-client/omit_metadata"]' \
34
rust/perspective-python/Cargo.toml
35
36
# Force the C++ archive containing the psp_* FFI exports to be whole-archive
37
# linked. Without this the rust linker leaves binding_api.cpp.o out and the
38
# cdylib comes out missing every psp_* symbol at import time. Only apply the
39
# modifier to libpsp; the recursive walk emits some library names twice
40
# (e.g. lz4) and rustc rejects duplicates that disagree on modifiers. Also
41
# teach the walk to skip libpsp so its modifier-less re-emission doesn't
42
# collide with the explicit `+whole-archive=psp` declaration.
43
sed -i 's|cargo:rustc-link-lib=static=psp|cargo:rustc-link-lib=static:+whole-archive,-bundle=psp|' \
44
rust/perspective-server/build.rs
45
sed -i 's|println!("cargo:rustc-link-lib=static={a}");|if a != "psp" { println!("cargo:rustc-link-lib=static={a}"); }|' \
46
rust/perspective-server/build.rs
47
48
# Use the system protobuf (Arch's /usr/bin/protoc + /usr/lib/libprotobuf.so)
49
# rather than have perspective's cmake fetch and build a vendored copy.
50
# Swap psp_build_dep("protobuf", ...) for find_package(Protobuf REQUIRED)
51
# in the protos sub-build, and drop the obsolete include-dir line that
52
# pointed into the vendored protobuf source tree.
53
sed -i 's|psp_build_dep("protobuf".*|find_package(Protobuf REQUIRED)|' \
54
rust/perspective-server/cpp/protos/CMakeLists.txt
55
sed -i '/target_include_directories(protos PRIVATE \${CMAKE_CURRENT_BINARY_DIR}\/protobuf-src\/src)/d' \
56
rust/perspective-server/cpp/protos/CMakeLists.txt
57
58
# Point perspective-server's cmake at system protoc instead of the vendored
59
# one from the protobuf-src crate (v27.1), so the generated .pb.cc matches
60
# the system libprotobuf headers/ABI.
61
perl -i -0pe 's|protoc\(\)\s*\.parent\(\)\s*\.expect\("protoc\(\) returned root path or empty string"\),|std::path::PathBuf::from("/usr/bin"),|' \
62
rust/perspective-server/build.rs
63
64
# The cmake walk in build.rs would normally link the vendored libprotobuf.a
65
# the bundled build produces. With find_package we link the system .so
66
# instead — emitted alongside the system absl dylibs below.
67
sed -i 's|cmake_link_deps(\&artifact_dir)?;|cmake_link_deps(\&artifact_dir)?;\n link_system_protobuf_absl()?;|' \
68
rust/perspective-server/build.rs
69
cat >> rust/perspective-server/build.rs <<'RUSTEOF'
70
71
fn link_system_protobuf_absl() -> Result<(), std::io::Error> {
72
// perspective's bundled protobuf picks up the *system* abseil-cpp via
73
// find_package() during its cmake build, so libprotobuf.so carries
74
// references to abseil's versioned (`lts_…`) mangled names. Link the
75
// system protobuf and every system libabsl_*.so dynamically so those
76
// references resolve at module-load time. Arch ships these as shared
77
// libs under /usr/lib/libabsl_*.so (each a symlink onto a versioned
78
// .so.N.M.P).
79
println!("cargo:rustc-link-lib=dylib=protobuf");
80
let lib_dir = std::path::Path::new("/usr/lib");
81
for entry in fs::read_dir(lib_dir)? {
82
let path = entry?.path();
83
let name = match path.file_name().and_then(|n| n.to_str()) {
84
Some(s) => s.to_string(),
85
None => continue,
86
};
87
if let Some(stem) = name.strip_prefix("lib").and_then(|s| s.strip_suffix(".so"))
88
&& stem.starts_with("absl_")
89
{
90
println!("cargo:rustc-link-lib=dylib={stem}");
91
}
92
}
93
Ok(())
94
}
95
RUSTEOF
96
97
cd rust/perspective-python
98
cp ../../LICENSE.md .
99
100
# Graft the prebuilt jupyterlab labextension (JS + perspective-viewer wasm)
101
# from the upstream PyPI wheel into our `data` directory so maturin packages
102
# it into our wheel under `usr/share/jupyter/labextensions/`. We only take
103
# the .data tree from the upstream wheel; everything else (the .so, the
104
# python sources, the dist-info) comes from our local build.
105
rm -rf "perspective_python-$pkgver.data"
106
bsdtar -xf "$srcdir/$_upstream_whl" "perspective_python-$pkgver.data/data/share/jupyter/*"
107
}
108
109
build() {
110
cd "$_pkgname-$pkgver/rust/perspective-python"
111
export PSP_ROOT_DIR=../..
112
# makepkg adds `-flto=auto` to CXX/CFLAGS by default. With only LTO bitcode
113
# in the .o files, the rust linker can't see the C++ symbols in libpsp.a et
114
# al. and the cdylib comes out without any psp_*/arrow/protobuf symbols.
115
# Force fat-LTO objects so each .o carries both the bitcode and normal ELF
116
# code; the linker then has something to pull in from the static archives.
117
export CFLAGS+=" -ffat-lto-objects"
118
export CXXFLAGS+=" -ffat-lto-objects"
119
maturin build --release --features=generate-proto,protobuf-src,omit_metadata --interpreter python
120
}
121
122
package() {
123
cd "$_pkgname-$pkgver"
124
python -m installer --destdir="$pkgdir" rust/target/wheels/*.whl
125
install -Dm644 LICENSE.md -t "$pkgdir/usr/share/licenses/$pkgname/"
126
}
127
Changes since previous scan
--- PKGBUILD @ 2026-07-24 00:02+++ PKGBUILD @ 2026-08-03 00:08@@ -1,4 +1,4 @@-# Maintainer: Guillaume Horel <ghorel@lmcg.com>+# Maintainer: Guillaume Horel <guillaume.horel@gmail.com> pkgname=python-perspective _pkgname=perspectiveScan history
| Scanned at (UTC) | Severity | Rules |
|---|---|---|
| 2026-08-03 00:08:14 | LOW | 2 |
| 2026-08-02 00:16:08 | LOW | 2 |
| 2026-08-01 00:11:18 | LOW | 2 |
| 2026-07-31 00:14:10 | LOW | 2 |
| 2026-07-30 00:17:23 | LOW | 2 |
| 2026-07-29 00:25:53 | LOW | 2 |
| 2026-07-28 00:07:28 | LOW | 2 |
| 2026-07-27 00:24:32 | LOW | 2 |
| 2026-07-26 00:07:32 | LOW | 2 |
| 2026-07-25 00:13:44 | LOW | 2 |
| 2026-07-24 13:27:52 | LOW | 2 |
| 2026-07-24 00:02:28 | LOW | 2 |
| 2026-07-23 00:14:47 | LOW | 2 |
| 2026-07-22 23:22:13 | LOW | 2 |