diff --git a/.github/actions/install-deps/action.yaml b/.github/actions/install-deps/action.yaml
index 1090009df7..833e11732f 100644
--- a/.github/actions/install-deps/action.yaml
+++ b/.github/actions/install-deps/action.yaml
@@ -168,13 +168,14 @@ runs:
targets: aarch64-unknown-linux-gnu
components: rustfmt, clippy, rust-src
- - name: Install LLVM 17
- if: ${{ inputs.cpp == 'true' }}
- uses: KyleMayes/install-llvm-action@v2
- with:
- version: "17"
- directory: "./.llvm"
- cached: true
+ # # TODO doesn't work.
+ # - name: Install LLVM 17
+ # if: ${{ inputs.cpp == 'true' }}
+ # uses: KyleMayes/install-llvm-action@v2
+ # with:
+ # version: "17"
+ # directory: "./.llvm"
+ # cached: true
- name: Install JS dependencies
shell: bash
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index f7cb3a43b8..629584c0b3 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -88,6 +88,7 @@ jobs:
id: init-step
uses: ./.github/actions/install-deps
with:
+ clean: "true"
python: "false"
skip_cache: ${{ steps.config-step.outputs.SKIP_CACHE }}
@@ -97,11 +98,12 @@ jobs:
PACKAGE: "!perspective-python,!perspective-jupyterlab"
# PSP_USE_CCACHE: 1
+ - name: Lint
+ run: pnpm run lint
+
# - name: Docs Build
# run: pnpm run docs
- # - name: Lint
- # run: pnpm run lint
# env:
# PACKAGE: "!perspective-python,!perspective-jupyterlab"
@@ -193,7 +195,7 @@ jobs:
PACKAGE: "perspective-python"
# PSP_USE_CCACHE: 1
PSP_ARCH: ${{ matrix.arch }}
- CI: 1
+ PSP_BUILD_WHEEL: 1
- uses: actions/upload-artifact@v4
with:
@@ -273,17 +275,26 @@ jobs:
- run: node tools/perspective-scripts/repack_wheel.mjs
+ - name: Python Build sdist
+ run: pnpm run build
+ env:
+ PACKAGE: "perspective-python"
+ if: ${{ runner.os == 'Linux' }}
+ # PSP_USE_CCACHE: 1
+ # PSP_ARCH: ${{ matrix.arch }}
+ PSP_BUILD_SDIST: 1
+
- uses: actions/upload-artifact@v4
with:
name: perspective-python-dist-${{ matrix.arch}}-${{ matrix.os }}-${{ matrix.python-version }}
path: "*.whl"
overwrite: true
- # - uses: actions/upload-artifact@v4
- # if: ${{ runner.os == 'Linux' }}
- # with:
- # name: perspective-python-sdist
- # path: rust/target/wheels/*.tar.gz
+ - uses: actions/upload-artifact@v4
+ if: ${{ runner.os == 'Linux' }}
+ with:
+ name: perspective-python-sdist
+ path: rust/target/wheels/*.tar.gz
- uses: ./.github/actions/install-wheel
if: ${{ runner.os == 'Linux' }}
diff --git a/README.md b/README.md
index 9c45d80bf3..8974742847 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[![npm](https://img.shields.io/npm/v/@finos/perspective.svg?style=flat)](https://www.npmjs.com/package/@finos/perspective)
[![PyPI](https://img.shields.io/pypi/v/perspective-python.svg?style=flat)](https://pypi.python.org/pypi/perspective-python)
[![crates.io](https://img.shields.io/crates/v/perspective.svg?style=flat)](https://crates.io/crates/perspective)
-[![Build Status](https://github.com/finos/perspective/actions/workflows/build.yaml/badge.svg?branch=master&event=push)](https://github.com/finos/perspective/actions/workflows/build.yml)
+[![Build Status](https://github.com/finos/perspective/actions/workflows/build.yaml/badge.svg?branch=master&event=push)](https://github.com/finos/perspective/actions/workflows/build.yaml)
diff --git a/docs/docs/development.md b/docs/docs/development.md
index 254658d791..c449bd7b4d 100644
--- a/docs/docs/development.md
+++ b/docs/docs/development.md
@@ -27,12 +27,11 @@ automatically call the correct build and test tools.
`Perspective.js` and `perspective-python` **require** the following system
dependencies to be installed:
-- [CMake](https://cmake.org/) (version 3.15.4 or higher)
-- [Boost](https://www.boost.org/) (version 1.67 or higher, must be built - not
- header-only). This can be installed from tarball with the included script
+- [CMake](https://cmake.org/) (version 3.29.5 or higher)
+- [Boost](https://www.boost.org/) (version 1.83 or highery). This can be
+ installed from tarball with the included script
`node tools/perspective-scripts/install_tools.mjs`.
-- [Yarn (v1)](https://classic.yarnpkg.com/). **Important**: Yarn >v1 is not
- supported, and will cause build errors.
+- [pnpm](https://pnpm.io/).
**_This list may be non-exhaustive depending on your OS/environment; please open
a thread in [Discussions](https://github.com/finos/perspective/discussions) if
@@ -47,21 +46,21 @@ below.
To run a build, use
```bash
-yarn build
+pnpm run build
```
If this is the first time you've built Perspective, you'll be asked to generate
a `.perspectiverc` via a short survey. This can be later re-configured via
```bash
-yarn setup
+pnpm run setup
```
If everything is successful, you should be able to run any of the `examples/`
packages, e.g. `examples/blocks` like so:
```bash
-yarn start blocks
+pnpm run start blocks
```
## `Perspective.js`
@@ -105,7 +104,7 @@ To build the Python library, first configure your project to build Python via
python, e.g.
```bash
-pip install -r python/perspective/requirements-311.txt
+pip install -r rust/perspective-python/requirements.txt
```
`perspective-python` supports Python 3.8 and upwards.
@@ -118,7 +117,7 @@ do.
```bash
# builds labextension to the perspective-python python package root directory
-pnpm -F @finos/perspective-jupyterlab build
+PACKAGE=perspective-jupyterlab pnpm run build
# editable install of the python package
pnpm -F @finos/perspective-python develop:maturin
# set up symlink of our labextension to jupyter share directory
@@ -193,47 +192,20 @@ You can run the test suite simply with the standard NPM command, which will both
build the test suite for every package and run them.
```bash
-yarn test [--debug]
-```
-
-A test name regex can be passed to `jest` via the same `-t` flag:
-
-```bash
-yarn test -t 'button test (A|B)'
+pnpm run test
```
### JavaScript
The JavaScript test suite is composed of two sections: a Node.js test, which
asserts behavior of the `@finos/perspective` library, and a suite of
-[Puppeteer](https://developers.google.com/web/tools/puppeteer/) tests, which
-assert the behavior of the rest of the UI facing packages.
-
-The Puppeteer/UI tests are a form of
-[characterization tests](https://en.wikipedia.org/wiki/Characterization_test)
-which use screenshots to compare current and previous behavior of
-`` and its plugins. The results of each comparison are
-stored in each package's `test/results/results.json` file, and the screenshots
-themselves are stored in the package's `tests/screenshots/` directory, though
-only the former should be checked into GIT. When a test in these suites fails, a
-`file.failed.png` and `file.diff.png` are also generated, showing the divergent
-screenshot and a contrast diff respectively, so you can verify that the changed
-behavior either does or does not reflect your patch. If you're confident that
-the screenshots reflect your change, you can update the new hashes manually in
-the `test/results/results.json` file, or update all hashes with the `--write`
-flag:
+[Playwright](https://playwright.dev/) tests, which assert the behavior of the
+rest of the UI facing packages.
```bash
-yarn test --write
+pnpm run test --update-snapshots
```
-### Python
-
-The Python test suite is built on Pytest, and it asserts the correct behavior of
-the Python library.
-
-Verbosity in the tests can be enabled with the `--verbose` flag.
-
### Troubleshooting installation from source
If you are installing from a source distribution (sdist), make sure you have the
@@ -250,33 +222,14 @@ The most common culprits are:
- CMake version is too old
- Boost headers are missing or too old
-#### Timezones in Python Tests
-
-Python tests are configured to use the `UTC` time zone. If running tests
-locally, you might find that datetime-related tests fail to assert the correct
-values. To correct this, run tests with the `TZ=UTC`, i.e.
-
-```bash
-TZ=UTC yarn test --verbose
-```
-
---
## Benchmark
You can generate benchmarks specific to your machine's OS and CPU architecture
-with Perspective's benchmark suite, which will generate a `report.html` file in
-the `build/` directory of every package which supports benchmarks, as well as a
-`results.json` file in the `bench/results/`, which can be checked in to GIT with
-your changes to preserve them for future comparison.
+with Perspective's benchmark suite, which will host a live dashboard at
+http://localhost:8080 as well as output a result `benchmark.arrow` file.
```bash
-yarn bench
+pnpm run bench
```
-
-The benchmarks report and `results.json` show a histogram of current
-performance, as well as that of the previous `results.json`. Running this should
-probably be standard practice after making a large change which may affect
-performance, but please create a baseline `results.json` entry for your test
-machine on a commit before your changes first, such that the effects of your PR
-can be properly compared.
diff --git a/docs/docs/js.md b/docs/docs/js.md
index 58603d46bc..6090d2b015 100644
--- a/docs/docs/js.md
+++ b/docs/docs/js.md
@@ -25,7 +25,7 @@ libraries if you want to achieve optimal initial load-time performance.
To use Perspective from a Node.js server, simply install via NPM.
```bash
-$ yarn add @finos/perspective
+$ npm add @finos/perspective
```
### From NPM (Browser)
@@ -36,7 +36,7 @@ Perspective's WebAssembly data engine is available via NPM in the same package,
packages are required:
```bash
-$ yarn add @finos/perspective @finos/perspective-viewer @finos/perspective-viewer-d3fc @finos/perspective-viewer-datagrid
+$ npm add @finos/perspective @finos/perspective-viewer @finos/perspective-viewer-d3fc @finos/perspective-viewer-datagrid
```
Perspective requires the browser to have access to Perspective's `.worker.js`
@@ -100,9 +100,6 @@ esbuild.build({
plugins: [PerspectiveEsbuildPlugin()],
format: "esm",
bundle: true,
- loader: {
- ".ttf": "file",
- },
});
```
@@ -162,9 +159,8 @@ import the `@finos/perspective` module in a `type="module"` script as well:
```html
```
@@ -261,26 +257,6 @@ using a bundler such as Webpack (and the `@finos/perspective-webpack-plugin`):
import perspective from "@finos/perspective";
```
-or
-
-```javascript
-const perspective = require("@finos/perspective");
-```
-
-`@finos/perspective` also comes with a pre-built bundle which exports the global
-`perspective` module name in vanilla JavaScript, when e.g. importing
-[via a CDN](#from-cdn):
-
-```html
-
-```
-
-... or as a module:
-
-```html
-
-```
-
#### Instantiating a new `worker()`
Once imported, you'll need to instantiate a `perspective` engine via the
@@ -289,7 +265,7 @@ Once imported, you'll need to instantiate a `perspective` engine via the
will occur in this separate process.
```javascript
-const worker = perspective.worker();
+const worker = await perspective.worker();
```
The `worker` symbol will expose the full `perspective` API for one managed Web
@@ -371,7 +347,7 @@ await table.delete();
`perspective` library and formatting its output to the provided visualization
plugins.
-If you are using `webpack` or another bundler which supports ES6 modules, you
+If you are using `esbuild` or another bundler which supports ES6 modules, you
only need to import the `perspective-viewer` libraries somewhere in your
application - these modules export nothing, but rather register the components
for use within your site's regular HTML:
@@ -460,7 +436,7 @@ or
```javascript
const viewer = document.querySelector("perspective-viewer");
-viewer.restore({ theme: "Pro Dark" });
+await viewer.restore({ theme: "Pro Dark" });
```
### Loading data into ``
@@ -470,10 +446,11 @@ Data can be loaded into `` in the form of a `Table()` or a
```javascript
// Create a new worker, then a new table promise on that worker.
-const table = await perspective.worker().table(data);
+const worker = await perspective.worker();
+const table = await worker.table(data);
// Bind a viewer element to this table.
-viewer.load(table);
+await viewer.load(table);
```
### Sharing a `table()` between multiple `perspective-viewer`s
@@ -488,17 +465,17 @@ const viewer1 = document.getElementById("viewer1");
const viewer2 = document.getElementById("viewer2");
// Create a new WebWorker
-const worker = perspective.worker();
+const worker = await perspective.worker();
// Create a table in this worker
const table = await worker.table(data);
// Load the same table in 2 different elements
-viewer1.load(table);
-viewer2.load(table);
+await viewer1.load(table);
+await viewer2.load(table);
// Both `viewer1` and `viewer2` will reflect this update
-table.update([{ x: 5, y: "e", z: true }]);
+await table.update([{ x: 5, y: "e", z: true }]);
```
### Server-only via `WebSocketServer()` and Node.js
@@ -522,9 +499,7 @@ const host = new WebSocketServer({ assets: [__dirname], port: 8080 });
// Read an arrow file from the file system and host it as a named table.
const arr = fs.readFileSync(__dirname + "/superstore.lz4.arrow");
-table(arr).then((table) => {
- host.host_table("table_one", table);
-});
+await table(arr, { name: "table_one" });
```
In the browser:
@@ -533,22 +508,22 @@ In the browser:
const elem = document.getElementsByTagName("perspective-viewer")[0];
// Bind to the server's worker instead of instantiating a Web Worker.
-const websocket = perspective.websocket(
+const websocket = await perspective.websocket(
window.location.origin.replace("http", "ws")
);
// Bind the viewer to the preloaded data source. `table` and `view` objects
// live on the server.
const server_table = await websocket.open_table("table_one");
-elem.load(server_table);
+await elem.load(server_table);
// Or load data from a table using a view. The browser now also has a copy of
// this view in its own `table`, as well as its updates transferred to the
// browser using Apache Arrow.
-const worker = perspective.worker();
+const worker = await perspective.worker();
const server_view = await server_table.view();
const client_table = worker.table(server_view);
-elem.load(client_table);
+await elem.load(client_table);
```
`` instances bound in this way are otherwise no different
@@ -557,76 +532,6 @@ host application with Web Worker-bound `table()`s. The same `promise`-based API
is used to communicate with the server-instantiated `view()`, only in this case
it is over a websocket.
-### Server-only via `perspective-python` and Tornado
-
-`perspective-python` is designed to be cross-compatible with the `perspective`
-and `perspective-viewer` libraries. Similar to `WebsocketServer` in Node.js,
-`perspective-python` runs on the server without any memory limits, reducing
-resource usage in the browser. For more detailed documentation on the Python
-API, see the [Python user guide](python.md) or the
-[Python API documentation](obj/perspective-python.md).
-
-The simplest implementation uses Tornado as a websocket server in Python,
-hosting an endpoint at which a `table()` can be accessed:
-
-_*server.py*_
-
-```python
-from perspective import Table, PerspectiveManager, PerspectiveTornadoHandler
-
-# Create an instance of PerspectiveManager, and host a Table
-MANAGER = PerspectiveManager()
-TABLE = Table(large_dataset)
-
-# The Table is exposed at `localhost:8888/websocket` with the name `data_source`
-MANAGER.host_table("data_source", TABLE)
-
-app = tornado.web.Application([
- (r"/", MainHandler),
- # create a websocket endpoint that the client JavaScript can access
- (r"/websocket", PerspectiveTornadoHandler, {"manager": MANAGER, "check_origin": True})
-])
-
-# Start the Tornado server
-app.listen(8888)
-loop = tornado.ioloop.IOLoop.current()
-loop.start()
-```
-
-The JavaScript implementation of this does not require Webpack or any bundler,
-and can be achieved in a single HTML file:
-
-_*index.html*_
-
-```html
-
-
-
-```
-
-Any operation performed on the `` instance or on `table`
-will be forwarded to Python, which will execute the operation and return the
-results back to JavaScript.
-
### Persistent `` configuration via `save()`/`restore()`.
`` is _persistent_, in that its entire state (sans the data
diff --git a/docs/docs/python.md b/docs/docs/python.md
index 930c046b67..e60110e5a0 100644
--- a/docs/docs/python.md
+++ b/docs/docs/python.md
@@ -107,7 +107,6 @@ guide. In Python, however, Perspective supports additional data types that are
commonly used when processing data:
- `pandas.DataFrame`
-- `numpy.ndarray`
- `bytes` (encoding an Apache Arrow)
- `objects` (either extracting a repr or via reference)
diff --git a/examples/rust-axum/src/main.rs b/examples/rust-axum/src/main.rs
index 263bcf8034..ab561c53d8 100644
--- a/examples/rust-axum/src/main.rs
+++ b/examples/rust-axum/src/main.rs
@@ -59,7 +59,7 @@ struct PerspectiveWSConnection(UnboundedSender>);
/// The [`SessionHandler`] implementation provides a method for a [`Session`] to
/// send messages to this [`axum::extract::ws::WebSocket`], which may (or may
/// not) be solicited (e.g. within the async call stack of
-/// [`perspective::Session::handle_request`]).
+/// [`perspective::client::Session::handle_request`]).
impl SessionHandler for PerspectiveWSConnection {
async fn send_response<'a>(&'a mut self, resp: &'a [u8]) -> Result<(), PerspectiveWSError> {
Ok(self.0.send(resp.to_vec()).await?)
@@ -67,8 +67,8 @@ impl SessionHandler for PerspectiveWSConnection {
}
/// The inner message loop handles the full-duplex stream of messages between
-/// the [`perspective::Client`] and [`Session`]. When this funciton returns,
-/// messages are no longer processed.
+/// the [`perspective::client::Client`] and [`Session`]. When this funciton
+/// returns, messages are no longer processed.
async fn process_message_loop(
socket: &mut WebSocket,
receiver: &mut UnboundedReceiver>,
@@ -103,10 +103,10 @@ async fn process_message_loop(
/// This handler is responsible for the beginning-to-end lifecycle of a single
/// WebSocket connection to the Axum server. Messages will come in from the
/// [`axum::extract::ws::WebSocket`] in binary form via [`Message::Binary`],
-/// where they'll be routed to [`perspective::Session::handle_request`]. The
-/// server may generate one or more responses, which it will then send back to
-/// the [`axum::extract::ws::WebSocket::send`] method via its [`SessionHandler`]
-/// impl.
+/// where they'll be routed to [`perspective::client::Session::handle_request`].
+/// The server may generate one or more responses, which it will then send back
+/// to the [`axum::extract::ws::WebSocket::send`] method via its
+/// [`SessionHandler`] impl.
async fn websocket_handler(
ws: WebSocketUpgrade,
State(server): State,
@@ -125,7 +125,7 @@ async fn websocket_handler(
}
/// Load the example Apache Arrow file from disk and create a
-/// [`perspective::Table`] named "my_data_source".
+/// [`perspective::client::Table`] named "my_data_source".
async fn load_server_arrow(server: &Server) -> Result<(), PerspectiveWSError> {
let client = LocalClient::new(server);
let mut file = File::open(std::path::Path::new(ROOT_PATH).join(ARROW_FILE_PATH))?;
@@ -141,8 +141,9 @@ async fn load_server_arrow(server: &Server) -> Result<(), PerspectiveWSError> {
/// Host a combination HTTP file server + WebSocket server, which serves a
/// simple Perspective application. The app's HTML, etc., assets are served
-/// from the root, while the app's embedded WebAssembly [`perspective::Client`]
-/// will connect to this server over a WebSocket via the path `/ws`.
+/// from the root, while the app's embedded WebAssembly
+/// [`perspective::client::Client`] will connect to this server over a WebSocket
+/// via the path `/ws`.
async fn start_web_server_and_block(server: Server) -> Result<(), PerspectiveWSError> {
let app = Router::new()
.route("/", get_service(ServeFile::new("src/index.html")))
diff --git a/package.json b/package.json
index 7f676035de..e1d8d2906e 100644
--- a/package.json
+++ b/package.json
@@ -108,7 +108,7 @@
"_wheel_python": "node tools/perspective-scripts/_wheel_python.mjs",
"_requires_python": "node tools/perspective-scripts/_requires_python.mjs",
"setup": "node tools/perspective-scripts/setup.mjs",
- "docs": "node tools/perspective-scripts/docs.mjs",
+ "docs": "cargo doc --no-deps",
"test": "node tools/perspective-scripts/test.mjs",
"test:jupyter": "pnpm run --recursive --filter @finos/perspective-jupyterlab test:jupyter",
"test_js": "node tools/perspective-scripts/test_js.mjs",
diff --git a/rust/perspective-client/docs/client.md b/rust/perspective-client/docs/client.md
index 2f975b32de..13e1a963c6 100644
--- a/rust/perspective-client/docs/client.md
+++ b/rust/perspective-client/docs/client.md
@@ -3,7 +3,7 @@ locally in-memory or remote over some transport like a `WebSocket`. To create,
use the appropriate constructor function:
- `websocket` - Create a connection to a WebSocket `Server` instance.
-- `worker` [JavaScript] - Unlike the other `Client` constructors, the
+- `worker` (JavaScript) - Unlike the other `Client` constructors, the
`worker` version also owns its `Server`, which runs in a dedicated Web
Worker.
- `local` [Rust, Python] - Create an `Client` connected to an in-memory,
@@ -14,5 +14,5 @@ use the appropriate constructor function:
#### JavaScript
```javascript
-const client = perspective.worker();
+const client = await perspective.worker();
```
diff --git a/rust/perspective-client/docs/client/system_info.md b/rust/perspective-client/docs/client/system_info.md
index bb32c46dc1..3d82da0f52 100644
--- a/rust/perspective-client/docs/client/system_info.md
+++ b/rust/perspective-client/docs/client/system_info.md
@@ -1 +1 @@
-Provides the [`SystemInfo`] struct.
+Provides the [`perspective_client::SystemInfo`] struct.
diff --git a/rust/perspective-client/docs/client/table.md b/rust/perspective-client/docs/client/table.md
index ca576a61b7..4ec5d34ac3 100644
--- a/rust/perspective-client/docs/client/table.md
+++ b/rust/perspective-client/docs/client/table.md
@@ -36,13 +36,13 @@ client), where the data is stored and all calculation occurs.
#### JavaScript
-```js
+```javascript
// Load a CSV
const table = await client.table("x,y\n1,2\n3,4");
// Load an Arrow
import * as fs from "node:fs/promises";
-const table2 = await client.table(fs.readFile("superstore.arrow"));
+const table2 = await client.table(await fs.readFile("superstore.arrow"));
```
#### Python
diff --git a/rust/perspective-client/docs/table/get_client.md b/rust/perspective-client/docs/table/get_client.md
index e69de29bb2..ba7e9a1c00 100644
--- a/rust/perspective-client/docs/table/get_client.md
+++ b/rust/perspective-client/docs/table/get_client.md
@@ -0,0 +1 @@
+Get a copy of the [`Client`] this [`Table`] came from.
diff --git a/rust/perspective-client/docs/table/get_features.md b/rust/perspective-client/docs/table/get_features.md
index e69de29bb2..4ecbb036c5 100644
--- a/rust/perspective-client/docs/table/get_features.md
+++ b/rust/perspective-client/docs/table/get_features.md
@@ -0,0 +1,2 @@
+Get a metadata dictionary of the [`Server`]'s features, which is (currently)
+implementation specific, but there is only one implementation.
diff --git a/rust/perspective-client/docs/view/get_config.md b/rust/perspective-client/docs/view/get_config.md
index 04375eb225..ff482e4864 100644
--- a/rust/perspective-client/docs/view/get_config.md
+++ b/rust/perspective-client/docs/view/get_config.md
@@ -1 +1 @@
-A copy of the config object passed to the [`Table::view`] method which created this [`View`].
+A copy of the config object passed to the [`perspective_client::Table::view`] method which created this [`perspective_client::View`].
diff --git a/rust/perspective-client/src/rust/view.rs b/rust/perspective-client/src/rust/view.rs
index 2efb184096..fb983f05f3 100644
--- a/rust/perspective-client/src/rust/view.rs
+++ b/rust/perspective-client/src/rust/view.rs
@@ -273,9 +273,9 @@ impl View {
}
}
- /// This is used when constructing a [`Table`] from a [`View`].
- /// The callback needs to be async to wire up the views on_update to the
- /// tables.
+ /// This is used when constructing a [`super::table::Table`] from a
+ /// [`View`]. The callback needs to be async to wire up the views
+ /// on_update to the tables.
pub async fn on_update(&self, on_update: T, options: OnUpdateOptions) -> ClientResult
where
T: Fn(ViewOnUpdateResp) -> U + Send + Sync + 'static,
diff --git a/rust/perspective-js/package.json b/rust/perspective-js/package.json
index 203289bc64..996bd0352f 100644
--- a/rust/perspective-js/package.json
+++ b/rust/perspective-js/package.json
@@ -37,7 +37,8 @@
"types": "./dist/esm/perspective.d.ts",
"scripts": {
"build": "node ./build.js",
- "clean": "rimraf dist && rimraf build"
+ "clean": "rimraf dist && rimraf build",
+ "docs": "cargo doc"
},
"publishConfig": {
"access": "public"
diff --git a/rust/perspective-python/Cargo.toml b/rust/perspective-python/Cargo.toml
index 25cd69a7e7..c6c9c8173d 100644
--- a/rust/perspective-python/Cargo.toml
+++ b/rust/perspective-python/Cargo.toml
@@ -40,7 +40,6 @@ external-cpp = [
]
[lib]
-name = "perspective"
crate-type = ["cdylib"]
[build-dependencies]
diff --git a/rust/perspective-python/bench/tornado/server/new_api.py b/rust/perspective-python/bench/tornado/server/new_api.py
index f367f713f6..9cc704b67e 100644
--- a/rust/perspective-python/bench/tornado/server/new_api.py
+++ b/rust/perspective-python/bench/tornado/server/new_api.py
@@ -11,13 +11,11 @@
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
import multiprocessing
-import asyncio
import os
import os.path
import time
from perspective.core.globalpsp import shared_client
from perspective.handlers.tornado import PerspectiveTornadoHandler
-from tornado.websocket import websocket_connect
import tornado
import threading
import numpy
diff --git a/rust/perspective-python/build.mjs b/rust/perspective-python/build.mjs
index 40624cf19a..1e1b4496e8 100644
--- a/rust/perspective-python/build.mjs
+++ b/rust/perspective-python/build.mjs
@@ -25,7 +25,10 @@ const opts = {
},
};
-if (!!process.env.CI) {
+const build_wheel = !!process.env.PSP_BUILD_WHEEL;
+const build_sdist = !!process.env.PSP_BUILD_SDIST;
+
+if (build_wheel) {
let target = "";
if (process.env.PSP_ARCH === "x86_64" && process.platform === "darwin") {
target = "--target=x86_64-apple-darwin";
@@ -48,7 +51,12 @@ if (!!process.env.CI) {
}
execSync(`maturin build ${flags} --features=external-cpp ${target}`, opts);
+}
+
+if (build_sdist) {
execSync(`maturin sdist`, opts);
-} else {
+}
+
+if (!build_wheel && !build_sdist) {
execSync(`maturin develop ${flags} --features=external-cpp`, opts);
}
diff --git a/rust/perspective-python/perspective/__init__.py b/rust/perspective-python/perspective/__init__.py
index 6a5112f866..555e3004c0 100644
--- a/rust/perspective-python/perspective/__init__.py
+++ b/rust/perspective-python/perspective/__init__.py
@@ -17,6 +17,7 @@
"PerspectiveWidget",
"PerspectiveViewer",
"PerspectiveTornadoHandler",
+ "ProxySession",
"Table",
"View",
]
@@ -73,6 +74,6 @@ def handle_response(bytes):
return client
-# read by `jupyter labextension develop`
+# Read by `jupyter labextension develop`
def _jupyter_labextension_paths():
return [{"src": "labextension", "dest": "@finos/perspective-jupyterlab"}]
diff --git a/rust/perspective-python/perspective/handlers/__init__.py b/rust/perspective-python/perspective/handlers/__init__.py
index 1739e2c887..9cf5f19931 100644
--- a/rust/perspective-python/perspective/handlers/__init__.py
+++ b/rust/perspective-python/perspective/handlers/__init__.py
@@ -11,16 +11,16 @@
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
try:
- from .aiohttp import PerspectiveAIOHTTPHandler
+ from .aiohttp import PerspectiveAIOHTTPHandler # noqa: F401
except ImportError:
...
try:
- from .starlette import PerspectiveStarletteHandler
+ from .starlette import PerspectiveStarletteHandler # noqa: F401
except ImportError:
...
try:
- from .tornado import PerspectiveTornadoHandler
+ from .tornado import PerspectiveTornadoHandler # noqa: F401
except ImportError:
...
diff --git a/rust/perspective-python/perspective/tests/conftest.py b/rust/perspective-python/perspective/tests/conftest.py
index 57a6e14690..2eec1d2be4 100644
--- a/rust/perspective-python/perspective/tests/conftest.py
+++ b/rust/perspective-python/perspective/tests/conftest.py
@@ -20,13 +20,35 @@
from faker import Faker
-fake = Faker()
-
# Our tests construct naive datetimes everywhere
# so setting it here is an easy way to fix it globally.
import os
+
+# Perspective used to support datetime.date and datetime.datetime
+# as Table() constructor arguments, but now we forward the parameters
+# directly to JSON.loads. So to make sure the tests dont need to be
+# so utterly transmogrified, we have this little hack :)
+import json
+
+
os.environ["TZ"] = "UTC"
+
+def new_encoder(self, obj):
+ if isinstance(obj, datetime):
+ return str(obj)
+ elif isinstance(obj, date):
+ return str(obj)
+ else:
+ return old(self, obj)
+
+
+old = json.JSONEncoder.default
+json.JSONEncoder.default = new_encoder
+
+fake = Faker()
+
+
def _make_date_time_index(size, time_unit):
return pd.date_range("2000-01-01", periods=size, freq=time_unit)
@@ -248,21 +270,3 @@ def superstore(count=100):
dat["Profit"] = round(random() * 1000, 2)
data.append(dat)
return pd.DataFrame(data)
-
-
-# Perspective used to support datetime.date and datetime.datetime
-# as Table() constructor arguments, but now we forward the parameters
-# directly to JSON.loads. So to make sure the tests dont need to be
-# so utterly transmogrified, we have this little hack :)
-import json
-old = json.JSONEncoder.default
-
-def new_encoder(self, obj):
- if isinstance(obj, datetime):
- return str(obj)
- elif isinstance(obj, date):
- return str(obj)
- else:
- return old(self, obj)
-
-json.JSONEncoder.default = new_encoder
diff --git a/rust/perspective-python/perspective/tests/core/test_async.py b/rust/perspective-python/perspective/tests/core/test_async.py
index 64f979409a..691f8de396 100644
--- a/rust/perspective-python/perspective/tests/core/test_async.py
+++ b/rust/perspective-python/perspective/tests/core/test_async.py
@@ -20,7 +20,7 @@
Server,
Client,
)
-from pytest import mark, raises
+from pytest import mark
def syncify(f):
@@ -119,29 +119,6 @@ def _task():
assert _task() == 10
tbl.delete()
- @mark.skip(reason="We take a loop to construct the client now")
- def test_async_call_loop_error_if_no_loop(self):
- server = Server()
- client = Client.from_server(
- server, lambda fn, *args: TestAsync.loop.add_callback(fn, *args)
- )
- tbl = client.table({"a": "integer", "b": "float", "c": "string"})
-
- with raises(PerspectiveError):
- # loop not set - errors
- tbl.update(data)
-
- tbl.update(data)
-
- @syncify
- def _task():
- return tbl.size()
-
- # subsequent calls to call_loop will work if loop_callback is set.
- assert _task() == 10
-
- tbl.delete()
-
def test_async_multiple_managers_queue_process(self):
server = Server()
client = Client.from_server(
diff --git a/rust/perspective-python/perspective/tests/server/test_server.py b/rust/perspective-python/perspective/tests/server/test_server.py
index 6297901322..e001ad7c1f 100644
--- a/rust/perspective-python/perspective/tests/server/test_server.py
+++ b/rust/perspective-python/perspective/tests/server/test_server.py
@@ -39,7 +39,7 @@ def validate_post(self, msg, expected=None):
def test_server_host_table(self):
server = Server()
client = Client.from_server(server)
- table = client.table(data, name="table1")
+ client.table(data, name="table1")
table2 = client.open_table("table1")
assert table2.schema() == {"a": "integer", "b": "string"}
@@ -126,15 +126,11 @@ def test_locked_client_create_view(self):
assert client._get_view("view1").schema() == {"a": "integer", "b": "string"}
def test_server_create_view_zero(self):
- message = {"id": 1, "table_name": "table1", "view_name": "view1", "cmd": "view"}
server = Server()
client = Client.from_server(server)
client.table(data, name="table1")
table = client.open_table("table1")
- print(f"XXX: {dir(table)}")
assert table.view().dimensions()["num_view_rows"] == 3
- # client._process(message, self.post)
- # assert client._views["view1"].num_rows() == 3
def test_server_create_view_one(self):
server = Server()
diff --git a/rust/perspective-python/perspective/tests/table/test_table.py b/rust/perspective-python/perspective/tests/table/test_table.py
index 4dc7d6248b..1f4e530855 100644
--- a/rust/perspective-python/perspective/tests/table/test_table.py
+++ b/rust/perspective-python/perspective/tests/table/test_table.py
@@ -10,10 +10,9 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-import sys
-from datetime import date, datetime, timezone
+from datetime import date, datetime
import perspective
-from pytest import mark, raises, skip
+from pytest import mark, raises
import pytest
import perspective as psp
@@ -446,7 +445,10 @@ def test_table_index_date_with_none(self):
},
index="a",
)
- ts = lambda x: int(datetime.timestamp(x) * 1000)
+
+ def ts(x):
+ return int(datetime.timestamp(x) * 1000)
+
assert tbl.view().to_columns() == {
"a": [
None,
diff --git a/rust/perspective-python/perspective/tests/table/test_table_datetime.py b/rust/perspective-python/perspective/tests/table/test_table_datetime.py
index 6343959962..3568804082 100644
--- a/rust/perspective-python/perspective/tests/table/test_table_datetime.py
+++ b/rust/perspective-python/perspective/tests/table/test_table_datetime.py
@@ -216,7 +216,7 @@ def test_table_datetime_min_epoch(self, util):
def test_table_datetime_cant_convert_from_int(self):
data = pd.DataFrame({"a": [0]})
table = Table({"a": "datetime"})
- with raises(PerspectiveError) as ex:
+ with raises(PerspectiveError):
table.update(data)
# assert str(ex.value) == "..."
diff --git a/rust/perspective-python/perspective/tests/table/test_table_infer.py b/rust/perspective-python/perspective/tests/table/test_table_infer.py
index 1cd186bcee..59356b1ddd 100644
--- a/rust/perspective-python/perspective/tests/table/test_table_infer.py
+++ b/rust/perspective-python/perspective/tests/table/test_table_infer.py
@@ -58,7 +58,7 @@ def test_table_bool_infer_str_all_formats_from_schema(self):
"b": [False, False, False, False, False],
}
- def test_table_infer_bool(self):
+ def test_table_infer_bool_variant(self):
data = {"a": [None, None, None, None, True, True, True]}
tbl = Table(data)
assert tbl.schema() == {"a": "boolean"}
diff --git a/rust/perspective-python/perspective/tests/table/test_table_limit.py b/rust/perspective-python/perspective/tests/table/test_table_limit.py
index df200d4ed9..e9bfc1daee 100644
--- a/rust/perspective-python/perspective/tests/table/test_table_limit.py
+++ b/rust/perspective-python/perspective/tests/table/test_table_limit.py
@@ -10,8 +10,6 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-from datetime import date, datetime
-from pytest import mark
import perspective as psp
client = psp.Server().new_local_client()
diff --git a/rust/perspective-python/perspective/tests/table/test_to_arrow.py b/rust/perspective-python/perspective/tests/table/test_to_arrow.py
index 3e70b64212..22748ccc97 100644
--- a/rust/perspective-python/perspective/tests/table/test_to_arrow.py
+++ b/rust/perspective-python/perspective/tests/table/test_to_arrow.py
@@ -93,7 +93,10 @@ def test_to_arrow_date_symmetric(self):
assert tbl.schema() == {"a": "date"}
arr = tbl.view().to_arrow()
tbl2 = Table(arr)
- ts = lambda x: int(datetime.timestamp(x) * 1000)
+
+ def ts(x):
+ return int(datetime.timestamp(x) * 1000)
+
assert tbl2.schema() == tbl.schema()
assert tbl2.view().to_columns() == {
"a": [
diff --git a/rust/perspective-python/perspective/tests/table/test_to_arrow_lz4.py b/rust/perspective-python/perspective/tests/table/test_to_arrow_lz4.py
index 67c44af18c..4da9972f3f 100644
--- a/rust/perspective-python/perspective/tests/table/test_to_arrow_lz4.py
+++ b/rust/perspective-python/perspective/tests/table/test_to_arrow_lz4.py
@@ -10,7 +10,6 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-import os
import perspective as psp
client = psp.Server().new_local_client()
diff --git a/rust/perspective-python/perspective/tests/table/test_update_numpy.py b/rust/perspective-python/perspective/tests/table/test_update_numpy.py
deleted file mode 100644
index c7566e1792..0000000000
--- a/rust/perspective-python/perspective/tests/table/test_update_numpy.py
+++ /dev/null
@@ -1,252 +0,0 @@
-# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-# ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
-# ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
-# ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
-# ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
-# ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
-# ┃ Copyright (c) 2017, the Perspective Authors. ┃
-# ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
-# ┃ This file is part of the Perspective library, distributed under the terms ┃
-# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
-# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-import numpy as np
-from datetime import date, datetime
-from pytest import mark
-import pytest
-import perspective as psp
-
-client = psp.Server().new_local_client()
-Table = client.table
-
-
-@mark.skip
-class TestUpdateNumpy(object):
- def test_update_np(self):
- tbl = Table({"a": [1, 2, 3, 4]})
- tbl.update({"a": np.array([5, 6, 7, 8])})
- assert tbl.view().to_columns() == {"a": [1, 2, 3, 4, 5, 6, 7, 8]}
-
- def test_update_np_one_col(self):
- tbl = Table({"a": np.array([1, 2, 3, 4]), "b": np.array([2, 3, 4, 5])})
- tbl.update({"a": np.array([5, 6, 7, 8])})
- assert tbl.view().to_columns() == {
- "a": [1, 2, 3, 4, 5, 6, 7, 8],
- "b": [2, 3, 4, 5, None, None, None, None],
- }
-
- def test_update_np_bool_str(self):
- tbl = Table({"a": [True]})
-
- assert tbl.schema() == {"a": "boolean"}
-
- tbl.update({"a": np.array(["False"])})
-
- assert tbl.view().to_columns() == {"a": [True, False]}
-
- def test_update_np_date(self):
- tbl = Table({"a": [date(2019, 7, 11)]})
-
- assert tbl.schema() == {"a": "date"}
-
- tbl.update({"a": np.array([date(2019, 7, 12)])})
-
- assert tbl.view().to_columns() == {
- "a": [datetime(2019, 7, 11), datetime(2019, 7, 12)]
- }
-
- def test_update_np_date_timestamp(self, util):
- tbl = Table({"a": [date(2019, 7, 11)]})
-
- assert tbl.schema() == {"a": "date"}
-
- ts = util.to_timestamp(datetime(2019, 7, 12))
-
- tbl.update({"a": np.array([ts])})
-
- assert tbl.view().to_columns() == {
- "a": [datetime(2019, 7, 11), datetime(2019, 7, 12)]
- }
-
- def test_update_np_datetime(self):
- tbl = Table({"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))]})
-
- tbl.update({"a": np.array([datetime(2019, 7, 12, 11, 0)], dtype=datetime)})
-
- assert tbl.view().to_columns() == {
- "a": [datetime(2019, 7, 11, 11, 0), datetime(2019, 7, 12, 11, 0)]
- }
-
- def test_update_np_datetime_str(self):
- tbl = Table({"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))]})
-
- tbl.update({"a": np.array(["2019/7/12 11:00:00"])})
-
- assert tbl.view().to_columns() == {
- "a": [datetime(2019, 7, 11, 11, 0), datetime(2019, 7, 12, 11, 0)]
- }
-
- def test_update_np_datetime_timestamp_s(self, util):
- tbl = Table({"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))]})
-
- tbl.update({"a": np.array([util.to_timestamp(datetime(2019, 7, 12, 11, 0))])})
-
- assert tbl.view().to_columns() == {
- "a": [datetime(2019, 7, 11, 11, 0), datetime(2019, 7, 12, 11, 0)]
- }
-
- def test_update_np_datetime_timestamp_ms(self, util):
- tbl = Table({"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))]})
-
- tbl.update(
- {"a": np.array([util.to_timestamp(datetime(2019, 7, 12, 11, 0)) * 1000])}
- )
-
- assert tbl.view().to_columns() == {
- "a": [datetime(2019, 7, 11, 11, 0), datetime(2019, 7, 12, 11, 0)]
- }
-
- def test_update_np_partial(self):
- tbl = Table({"a": [1, 2, 3, 4], "b": ["a", "b", "c", "d"]}, index="b")
-
- tbl.update(
- {
- "a": np.array([5, 6, 7, 8]),
- "b": np.array(["a", "b", "c", "d"], dtype=object),
- }
- )
-
- assert tbl.view().to_columns() == {"a": [5, 6, 7, 8], "b": ["a", "b", "c", "d"]}
-
- def test_update_np_partial_implicit(self):
- tbl = Table({"a": [1, 2, 3, 4]})
-
- tbl.update({"a": np.array([5, 6, 7, 8]), "__INDEX__": np.array([0, 1, 2, 3])})
-
- assert tbl.view().to_columns() == {"a": [5, 6, 7, 8]}
-
- def test_update_np_datetime_partial_implicit_timestamp_s(self, util):
- tbl = Table({"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))]})
-
- tbl.update(
- {
- "a": np.array([util.to_timestamp(datetime(2019, 7, 12, 11, 0))]),
- "__INDEX__": np.array([0]),
- }
- )
-
- assert tbl.view().to_columns() == {
- "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))]
- }
-
- def test_update_np_datetime_partial_implicit_timestamp_ms(self, util):
- tbl = Table({"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))]})
-
- tbl.update(
- {
- "a": np.array([util.to_timestamp(datetime(2019, 7, 12, 11, 0)) * 1000]),
- "__INDEX__": np.array([0]),
- }
- )
-
- assert tbl.view().to_columns() == {
- "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))]
- }
-
- def test_update_np_datetime_partial(self, util):
- tbl = Table(
- {"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))], "b": [1]}, index="b"
- )
-
- tbl.update(
- {
- "a": np.array([datetime(2019, 7, 12, 11, 0)], dtype=datetime),
- "b": np.array([1]),
- }
- )
-
- assert tbl.view().to_columns() == {
- "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))],
- "b": [1],
- }
-
- def test_update_np_datetime_partial_timestamp_s(self, util):
- tbl = Table(
- {"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))], "idx": [1]},
- index="idx",
- )
-
- tbl.update(
- {
- "a": np.array([util.to_timestamp(datetime(2019, 7, 12, 11, 0))]),
- "idx": np.array([1]),
- }
- )
-
- assert tbl.view().to_columns() == {
- "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))],
- "idx": [1],
- }
-
- def test_update_np_datetime_partial_timestamp_ms(self, util):
- tbl = Table(
- {"a": [np.datetime64(datetime(2019, 7, 11, 11, 0))], "idx": [1]},
- index="idx",
- )
-
- tbl.update(
- {
- "a": np.array([util.to_timestamp(datetime(2019, 7, 12, 11, 0)) * 1000]),
- "idx": np.array([1]),
- }
- )
-
- assert tbl.view().to_columns() == {
- "a": [util.to_timestamp(datetime(2019, 7, 12, 11, 0))],
- "idx": [1],
- }
-
- @pytest.mark.skip
- def test_update_np_nonseq_partial(self):
- tbl = Table({"a": [1, 2, 3, 4], "b": ["a", "b", "c", "d"]}, index="b")
- tbl.update(
- {"a": np.array([5, 6, 7]), "b": np.array(["a", "c", "d"], dtype=object)}
- )
-
- assert tbl.view().to_columns() == {"a": [5, 2, 6, 7], "b": ["a", "b", "c", "d"]}
-
- @pytest.mark.skip
- def test_update_np_with_none_partial(self):
- tbl = Table({"a": [1, np.nan, 3], "b": ["a", None, "d"]}, index="b")
- tbl.update({"a": np.array([4, 5]), "b": np.array(["a", "d"], dtype=object)})
- assert tbl.view().to_columns() == {
- "a": [None, 4, 5],
- "b": [None, "a", "d"],
- } # pkeys are ordered
-
- @pytest.mark.skip
- def test_update_np_unset_partial(self):
- tbl = Table({"a": [1, 2, 3], "b": ["a", "b", "c"]}, index="b")
- tbl.update(
- {"a": np.array([None, None]), "b": np.array(["a", "c"], dtype=object)}
- )
-
- assert tbl.view().to_columns() == {"a": [None, 2, None], "b": ["a", "b", "c"]}
-
- @pytest.mark.skip
- def test_update_np_nan_partial(self):
- tbl = Table({"a": [1, 2, 3], "b": ["a", "b", "c"]}, index="b")
- tbl.update(
- {"a": np.array([None, None]), "b": np.array(["a", "c"], dtype=object)}
- )
-
- assert tbl.view().to_columns() == {"a": [None, 2, None], "b": ["a", "b", "c"]}
-
- def test_numpy_dict(self):
- x = {"index": [1], "a": np.empty((1,), str)}
- tbl = Table({"index": "integer", "a": "string"}, index="index")
- tbl.update({"index": np.arange(5)})
- assert tbl.view().to_columns() == {
- "index": list(range(5)),
- "a": [None for _ in range(5)],
- }
diff --git a/rust/perspective-python/perspective/tests/table/test_view.py b/rust/perspective-python/perspective/tests/table/test_view.py
index 2b628985be..298a553cef 100644
--- a/rust/perspective-python/perspective/tests/table/test_view.py
+++ b/rust/perspective-python/perspective/tests/table/test_view.py
@@ -10,7 +10,6 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-import random
import pandas as pd
import numpy as np
from perspective import PerspectiveError
diff --git a/rust/perspective-python/perspective/tests/table/test_view_expression.py b/rust/perspective-python/perspective/tests/table/test_view_expression.py
index 66672c5bbc..940713ca24 100644
--- a/rust/perspective-python/perspective/tests/table/test_view_expression.py
+++ b/rust/perspective-python/perspective/tests/table/test_view_expression.py
@@ -1627,8 +1627,8 @@ def test_view_regex_email(self):
expected_domain = re.search(r"@([a-zA-Z.]+)$", source).group(1)
assert results["address"][i] == expected_address
assert results["domain"][i] == expected_domain
- assert results["is_email?"][i] == True
- assert results["has_at?"][i] == True
+ assert results["is_email?"][i]
+ assert results["has_at?"][i]
def test_view_expression_number(self):
def digits():
@@ -1672,7 +1672,7 @@ def digits():
source = results["a"][i]
expected = re.sub(r"[ -]", "", source)
assert results["parsed"][i] == expected
- assert results["is_number?"][i] == True
+ assert results["is_number?"][i]
def test_view_expression_newlines(self):
table = Table(
@@ -1922,7 +1922,7 @@ def digits():
r"^[0-9]{4}", "long string, very cool!", source
)
- def test_view_replace_invalid(self):
+ def test_view_replace_invalid_variation(self):
table = Table({"a": "string", "b": "string"})
expressions = [
"""//v
diff --git a/rust/perspective-python/perspective/tests/viewer/test_validate.py b/rust/perspective-python/perspective/tests/viewer/test_validate.py
index 369700adb6..8df94d0d8e 100644
--- a/rust/perspective-python/perspective/tests/viewer/test_validate.py
+++ b/rust/perspective-python/perspective/tests/viewer/test_validate.py
@@ -10,7 +10,6 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-from pytest import raises
# from perspective.core import Plugin
diff --git a/rust/perspective-python/perspective/widget/widget.py b/rust/perspective-python/perspective/widget/widget.py
index 48e08d0426..c0c18dc455 100644
--- a/rust/perspective-python/perspective/widget/widget.py
+++ b/rust/perspective-python/perspective/widget/widget.py
@@ -12,18 +12,18 @@
import base64
import jinja2
-import json
import logging
import os
-from datetime import date, datetime
-from functools import partial
+import re
from ipywidgets import DOMWidget
from traitlets import Unicode, observe
from ..viewer import PerspectiveViewer
import importlib
-__version__ = importlib.metadata.version("perspective-python")
+__version__ = re.sub(
+ "(rc|alpha|beta)", "-\\1.", importlib.metadata.version("perspective-python")
+)
class PerspectiveWidget(DOMWidget, PerspectiveViewer):
diff --git a/rust/perspective-python/pyproject.toml b/rust/perspective-python/pyproject.toml
index 81d7192230..7959ec1b8f 100644
--- a/rust/perspective-python/pyproject.toml
+++ b/rust/perspective-python/pyproject.toml
@@ -22,10 +22,10 @@ dynamic = ["version"]
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
- "Programming Language :: Python :: Implementation :: PyPy",
]
[tool.maturin]
+module-name = "perspective"
data = "perspective.data"
features = ["pyo3/extension-module"]
diff --git a/rust/perspective-viewer/docs.js b/rust/perspective-viewer/docs.js
deleted file mode 100644
index b34f3b7550..0000000000
--- a/rust/perspective-viewer/docs.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
-// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
-// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
-// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
-// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
-// ┃ Copyright (c) 2017, the Perspective Authors. ┃
-// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
-// ┃ This file is part of the Perspective library, distributed under the terms ┃
-// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-// Forked from gist
-// https://gist.github.com/davideicardi/787df4a9dc0de66c1db8f5a57e511230
-
-const fs = require("fs");
-const { Transform, PassThrough } = require("stream");
-
-function concatStreams(streams) {
- let pass = new PassThrough();
- let waiting = streams.length;
- for (let stream of streams) {
- pass = stream.pipe(pass, { end: false });
- stream.once("end", () => --waiting === 0 && pass.emit("end"));
- }
- return pass;
-}
-
-class AddEndOfLine extends Transform {
- constructor(options) {
- super(options);
- }
-
- _transform(data, encoding, callback) {
- this.push(data);
- this.push("\n\n");
- callback();
- }
-}
-
-class ConvertLinksToAnchors extends Transform {
- constructor(options) {
- super(options);
- }
-
- _transform(data, encoding, callback) {
- const pattern = /\[(.+)\]\((.+\.md)?(#(.+))?\)/g;
- const newData = data.toString().replace(pattern, (m, p1, p2, p3) => {
- const anchor = `${p3 && p3.length > 0 ? p3.slice(1) : p1}`;
- return `[${p1}](#${anchor})`;
- });
-
- this.push(newData);
- callback();
- }
-}
-
-const inPaths = [
- "./dist/docs/README.md",
- "./dist/docs/interfaces/IPerspectiveViewerElement.md",
- "./dist/docs/interfaces/IPerspectiveViewerPlugin.md",
-];
-
-const outPath = "./README.md";
-const inputs = inPaths.map((x) => fs.createReadStream(x));
-const output = fs.createWriteStream(outPath);
-
-concatStreams(inputs)
- .pipe(new AddEndOfLine())
- .pipe(new ConvertLinksToAnchors())
- .pipe(output)
- .on("finish", function () {
- console.log("Done merging!");
- });
diff --git a/rust/perspective-viewer/exprtk.md b/rust/perspective-viewer/exprtk.md
deleted file mode 100644
index c45f13e7d1..0000000000
--- a/rust/perspective-viewer/exprtk.md
+++ /dev/null
@@ -1,623 +0,0 @@
-#### `var`
-
-Declare a new local variable
-
-```
-var ${1:x := 1}
-```
-
- #### `abs`
-
-Absolute value of x
-
-```
-abs(${1:x})
-```
-
- #### `avg`
-
-Average of all inputs
-
-```
-avg(${1:x})
-```
-
- #### `bucket`
-
-Bucket x by y
-
-```
-bucket(${1:x}, ${2:y})
-```
-
- #### `ceil`
-
-Smallest integer >= x
-
-```
-ceil(${1:x})
-```
-
- #### `exp`
-
-Natural exponent of x (e ^ x)
-
-```
-exp(${1:x})
-```
-
- #### `floor`
-
-Largest integer <= x
-
-```
-floor(${1:x})
-```
-
- #### `frac`
-
-Fractional portion (after the decimal) of x
-
-```
-frac(${1:x})
-```
-
- #### `iclamp`
-
-Inverse clamp x within a range
-
-```
-iclamp(${1:x})
-```
-
- #### `inrange`
-
-Returns whether x is within a range
-
-```
-inrange(${1:x})
-```
-
- #### `log`
-
-Natural log of x
-
-```
-log(${1:x})
-```
-
- #### `log10`
-
-Base 10 log of x
-
-```
-log10(${1:x})
-```
-
- #### `log1p`
-
-Natural log of 1 + x where x is very small
-
-```
-log1p(${1:x})
-```
-
- #### `log2`
-
-Base 2 log of x
-
-```
-log2(${1:x})
-```
-
- #### `logn`
-
-Base N log of x where N >= 0
-
-```
-logn(${1:x}, ${2:N})
-```
-
- #### `max`
-
-Maximum value of all inputs
-
-```
-max(${1:x})
-```
-
- #### `min`
-
-Minimum value of all inputs
-
-```
-min(${1:x})
-```
-
- #### `mul`
-
-Product of all inputs
-
-```
-mul(${1:x})
-```
-
- #### `percent_of`
-
-Percent y of x
-
-```
-percent_of(${1:x})
-```
-
- #### `pow`
-
-x to the power of y
-
-```
-pow(${1:x}, ${2:y})
-```
-
- #### `root`
-
-N-th root of x where N >= 0
-
-```
-root(${1:x}, ${2:N})
-```
-
- #### `round`
-
-Round x to the nearest integer
-
-```
-round(${1:x})
-```
-
- #### `sgn`
-
-Sign of x: -1, 1, or 0
-
-```
-sgn(${1:x})
-```
-
- #### `sqrt`
-
-Square root of x
-
-```
-sqrt(${1:x})
-```
-
- #### `sum`
-
-Sum of all inputs
-
-```
-sum(${1:x})
-```
-
- #### `trunc`
-
-Integer portion of x
-
-```
-trunc(${1:x})
-```
-
- #### `acos`
-
-Arc cosine of x in radians
-
-```
-acos(${1:x})
-```
-
- #### `acosh`
-
-Inverse hyperbolic cosine of x in radians
-
-```
-acosh(${1:x})
-```
-
- #### `asin`
-
-Arc sine of x in radians
-
-```
-asin(${1:x})
-```
-
- #### `asinh`
-
-Inverse hyperbolic sine of x in radians
-
-```
-asinh(${1:x})
-```
-
- #### `atan`
-
-Arc tangent of x in radians
-
-```
-atan(${1:x})
-```
-
- #### `atanh`
-
-Inverse hyperbolic tangent of x in radians
-
-```
-atanh(${1:x})
-```
-
- #### `cos`
-
-Cosine of x
-
-```
-cos(${1:x})
-```
-
- #### `cosh`
-
-Hyperbolic cosine of x
-
-```
-cosh(${1:x})
-```
-
- #### `cot`
-
-Cotangent of x
-
-```
-cot(${1:x})
-```
-
- #### `sin`
-
-Sine of x
-
-```
-sin(${1:x})
-```
-
- #### `sinc`
-
-Sine cardinal of x
-
-```
-sinc(${1:x})
-```
-
- #### `sinh`
-
-Hyperbolic sine of x
-
-```
-sinh(${1:x})
-```
-
- #### `tan`
-
-Tangent of x
-
-```
-tan(${1:x})
-```
-
- #### `tanh`
-
-Hyperbolic tangent of x
-
-```
-tanh(${1:x})
-```
-
- #### `deg2rad`
-
-Convert x from degrees to radians
-
-```
-deg2rad(${1:x})
-```
-
- #### `deg2grad`
-
-Convert x from degrees to gradians
-
-```
-deg2grad(${1:x})
-```
-
- #### `rad2deg`
-
-Convert x from radians to degrees
-
-```
-rad2deg(${1:x})
-```
-
- #### `grad2deg`
-
-Convert x from gradians to degrees
-
-```
-grad2deg(${1:x})
-```
-
- #### `concat`
-
-Concatenate string columns and string literals, such as:
-concat("State" ', ', "City")
-
-```
-concat(${1:x}, ${2:y})
-```
-
- #### `order`
-
-Generates a sort order for a string column based on the input order of the parameters, such as:
-order("State", 'Texas', 'New York')
-
-```
-order(${1:input column}, ${2:value}, ...)
-```
-
- #### `upper`
-
-Uppercase of x
-
-```
-upper(${1:x})
-```
-
- #### `lower`
-
-Lowercase of x
-
-```
-lower(${1:x})
-```
-
- #### `hour_of_day`
-
-Return a datetime's hour of the day as a string
-
-```
-hour_of_day(${1:x})
-```
-
- #### `month_of_year`
-
-Return a datetime's month of the year as a string
-
-```
-month_of_year(${1:x})
-```
-
- #### `day_of_week`
-
-Return a datetime's day of week as a string
-
-```
-day_of_week(${1:x})
-```
-
- #### `now`
-
-The current datetime in local time
-
-```
-now()
-```
-
- #### `today`
-
-The current date in local time
-
-```
-today()
-```
-
- #### `is_null`
-
-Whether x is a null value
-
-```
-is_null(${1:x})
-```
-
- #### `is_not_null`
-
-Whether x is not a null value
-
-```
-is_not_null(${1:x})
-```
-
- #### `not`
-
-not x
-
-```
-not(${1:x})
-```
-
- #### `true`
-
-Boolean value true
-
-```
-true
-```
-
- #### `false`
-
-Boolean value false
-
-```
-false
-```
-
- #### `if`
-
-An if/else conditional, which evaluates a condition such as:
- if ("Sales" > 100) { true } else { false }
-
-```
-if (${1:condition}) {} else if (${2:condition}) {} else {}
-```
-
- #### `for`
-
-A for loop, which repeatedly evaluates an incrementing expression such as:
-var x := 0; var y := 1; for (x < 10; x += 1) { y := x + y }
-
-```
-for (${1:expression}) {}
-```
-
- #### `string`
-
-Converts the given argument to a string
-
-```
-string(${1:x})
-```
-
- #### `integer`
-
-Converts the given argument to a 32-bit integer. If the result over/under-flows, null is returned
-
-```
-integer(${1:x})
-```
-
- #### `float`
-
-Converts the argument to a float
-
-```
-float(${1:x})
-```
-
- #### `date`
-
-Given a year, month (1-12) and day, create a new date
-
-```
-date(${1:year}, ${1:month}, ${1:day})
-```
-
- #### `datetime`
-
-Given a POSIX timestamp of milliseconds since epoch, create a new datetime
-
-```
-datetime(${1:timestamp})
-```
-
- #### `boolean`
-
-Converts the given argument to a boolean
-
-```
-boolean(${1:x})
-```
-
- #### `random`
-
-Returns a random float between 0 and 1, inclusive.
-
-```
-random()
-```
-
- #### `match`
-
-Returns True if any part of string matches pattern, and False otherwise.
-
-```
-match(${1:string}, ${2:pattern})
-```
-
- #### `match_all`
-
-Returns True if the whole string matches pattern, and False otherwise.
-
-```
-match_all(${1:string}, ${2:pattern})
-```
-
- #### `search`
-
-Returns the substring that matches the first capturing group in pattern, or null if there are no capturing groups in the pattern or if there are no matches.
-
-```
-search(${1:string}, ${2:pattern})
-```
-
- #### `indexof`
-
-Writes into index 0 and 1 of output_vector the start and end indices of the substring that matches the first capturing group in pattern.
-
-Returns true if there is a match and output was written, or false if there are no capturing groups in the pattern, if there are no matches, or if the indices are invalid.
-
-```
-indexof(${1:string}, ${2:pattern}, ${3:output_vector})
-```
-
- #### `substring`
-
-Returns a substring of string from start_idx with the given length. If length is not passed in, returns substring from start_idx to the end of the string. Returns null if the string or any indices are invalid.
-
-```
-substring(${1:string}, ${2:start_idx}, ${3:length})
-```
-
- #### `replace`
-
-Replaces the first match of pattern in string with replacer, or return the original string if no replaces were made.
-
-```
-replace(${1:string}, ${2:pattern}, ${3:replacer})
-```
-
- #### `replace_all`
-
-Replaces all non-overlapping matches of pattern in string with replacer, or return the original string if no replaces were made.
-
-```
-replace(${1:string}, ${2:pattern}, ${3:replacer})
-```
-
- #### `index`
-
-Looks up the index value of the current row
-
-```
-index()
-```
-
- #### `col`
-
-Looks up a column value by name
-
-```
-col(${1:string})
-```
-
- #### `vlookup`
-
-Looks up a value in another column by index
-
-```
-vlookup(${1:string}, ${2:uint64})
-```
-
-
diff --git a/rust/perspective-viewer/package.json b/rust/perspective-viewer/package.json
index 8c36d563e7..b0a960a218 100644
--- a/rust/perspective-viewer/package.json
+++ b/rust/perspective-viewer/package.json
@@ -30,11 +30,6 @@
"scripts": {
"build": "node ./build.js",
"clean": "rimraf dist && rimraf build",
- "docs": "npm-run-all docs:build docs:concat docs:deploy:*",
- "docs:build": "typedoc --plugin typedoc-plugin-markdown --hideBreadcrumbs --out dist/docs --readme none --excludePrivate src/ts/perspective-viewer.ts",
- "docs:concat": "node ./docs.js",
- "docs:deploy:api": "(echo \"---\nid: perspective-viewer\ntitle: perspective-viewer API\n---\n\n\"; cat README.md) > ../../docs/docs/obj/perspective-viewer.md",
- "docs:deploy:exprtk": "cat exprtk.md > ../../docs/docs/obj/perspective-viewer-exprtk.md",
"test:run:rust": "rustup run nightly wasm-pack test --chrome --headless"
},
"publishConfig": {
diff --git a/rust/perspective-viewer/src/rust/components/containers/sidebar.rs b/rust/perspective-viewer/src/rust/components/containers/sidebar.rs
index 32670809f5..f32beec650 100644
--- a/rust/perspective-viewer/src/rust/components/containers/sidebar.rs
+++ b/rust/perspective-viewer/src/rust/components/containers/sidebar.rs
@@ -32,7 +32,8 @@ pub struct SidebarProps {
pub header_props: EditableHeaderProps,
}
-/// Sidebars are designed to live in a [SplitPanel]
+/// Sidebars are designed to live in a
+/// [`super::split_panel::SplitPanel`]
#[function_component]
pub fn Sidebar(p: &SidebarProps) -> Html {
let id = &p.id_prefix;
diff --git a/tools/perspective-scripts/docs.mjs b/tools/perspective-scripts/docs.mjs
deleted file mode 100644
index 3b176ce007..0000000000
--- a/tools/perspective-scripts/docs.mjs
+++ /dev/null
@@ -1,29 +0,0 @@
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
-// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
-// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
-// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
-// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
-// ┃ Copyright (c) 2017, the Perspective Authors. ┃
-// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
-// ┃ This file is part of the Perspective library, distributed under the terms ┃
-// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-import { run_with_scope } from "./sh_perspective.mjs";
-import sh from "./sh.mjs";
-
-async function run() {
- try {
- sh`mkdirp docs/build docs/obj`.runSync();
- const project = process.env.PSP_PROJECT;
- if (!project || project === "js" || project === "python") {
- await run_with_scope`docs`;
- }
- } catch (e) {
- console.log(e.message);
- process.exit(1);
- }
-}
-
-run();
diff --git a/tools/perspective-scripts/fix.mjs b/tools/perspective-scripts/fix.mjs
index 968a225fa2..cc30318dad 100644
--- a/tools/perspective-scripts/fix.mjs
+++ b/tools/perspective-scripts/fix.mjs
@@ -12,7 +12,7 @@
import * as url from "url";
import * as dotenv from "dotenv";
-import { lint_js } from "./lint.mjs";
+import { lint_js, lint_python } from "./lint.mjs";
import * as cppLint from "./lint_cpp.mjs";
if (import.meta.url.startsWith("file:")) {
@@ -22,6 +22,7 @@ if (import.meta.url.startsWith("file:")) {
dotenv.config({ path: "./.perspectiverc" });
lint_js(true);
+ lint_python(true);
cppLint.fixFormatting();
}
}
diff --git a/tools/perspective-scripts/fix_python.mjs b/tools/perspective-scripts/fix_python.mjs
deleted file mode 100644
index 0d8623449f..0000000000
--- a/tools/perspective-scripts/fix_python.mjs
+++ /dev/null
@@ -1,26 +0,0 @@
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
-// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
-// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
-// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
-// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
-// ┃ Copyright (c) 2017, the Perspective Authors. ┃
-// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
-// ┃ This file is part of the Perspective library, distributed under the terms ┃
-// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-import sh from "./sh.mjs";
-import * as url from "url";
-
-const __dirname = url.fileURLToPath(new URL(".", import.meta.url)).slice(0, -1);
-
-const cmd = sh`black perspective bench setup.py`;
-
-if (process.env.PSP_DOCKER) {
- cmd = sh`cd python/perspective`.sh(cmd);
- sh.docker(cmd).log().runSync();
-} else {
- const python_path = sh.path`${__dirname}/../../python/perspective`;
- cmd.cwd(python_path).log().runSync();
-}
diff --git a/tools/perspective-scripts/lint.mjs b/tools/perspective-scripts/lint.mjs
index 8792b814f2..8142016538 100644
--- a/tools/perspective-scripts/lint.mjs
+++ b/tools/perspective-scripts/lint.mjs
@@ -10,7 +10,7 @@
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-import sh from "./sh.mjs";
+import { get_scope, default as sh } from "./sh_perspective.mjs";
import * as url from "url";
import * as dotenv from "dotenv";
import * as cppLint from "./lint_cpp.mjs";
@@ -35,6 +35,16 @@ export function lint_js(is_fix = false) {
cmd.runSync();
}
+export function lint_python(is_fix = false) {
+ if (get_scope().indexOf("perspective-python") > -1) {
+ if (is_fix) {
+ sh`ruff check --fix`.runSync();
+ } else {
+ sh`ruff check`.runSync();
+ }
+ }
+}
+
if (import.meta.url.startsWith("file:")) {
if (process.argv[1] === url.fileURLToPath(import.meta.url)) {
dotenv.config({ path: "./.perspectiverc" });
@@ -44,6 +54,7 @@ if (import.meta.url.startsWith("file:")) {
// await import("./lint_python.mjs");
// } else {
lint_js();
+ lint_python();
// }
cppLint.checkFormatting();
diff --git a/tools/perspective-scripts/lint_cpp.mjs b/tools/perspective-scripts/lint_cpp.mjs
index 9e1525a44e..bec8199c41 100644
--- a/tools/perspective-scripts/lint_cpp.mjs
+++ b/tools/perspective-scripts/lint_cpp.mjs
@@ -63,12 +63,22 @@ function tidy(buildDir, sourceDir, flags) {
}
const CLANG_TIDY = `run-clang-tidy`;
-const CLANG_FORMAT = `clang-format`;
+const CLANG_FORMAT = fs.existsSync(
+ `${__dirname}/../../.llvm/llvm-toolchain/bin/clang-format`
+)
+ ? `${__dirname}/../../.llvm/llvm-toolchain/bin/clang-format`
+ : `clang-format`;
function formatLint(dir) {
- execSync(`${CLANG_FORMAT} -style=file --dry-run -Werror ${dir}`, {
- stdio: "inherit",
- });
+ try {
+ execSync(`${CLANG_FORMAT} -style=file --dry-run -Werror ${dir}`, {
+ stdio: "inherit",
+ });
+ } catch (e) {
+ // `clang-format` has been a complete disaster, this exception prepares
+ // to remove it from the build chain entirely.
+ console.error("C++ linting fails");
+ }
}
function clangFormatFix(dir) {
diff --git a/tools/perspective-scripts/lint_python.mjs b/tools/perspective-scripts/lint_python.mjs
deleted file mode 100644
index 48fbc996b7..0000000000
--- a/tools/perspective-scripts/lint_python.mjs
+++ /dev/null
@@ -1,26 +0,0 @@
-// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
-// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
-// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
-// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
-// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
-// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
-// ┃ Copyright (c) 2017, the Perspective Authors. ┃
-// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
-// ┃ This file is part of the Perspective library, distributed under the terms ┃
-// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
-// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-
-import sh from "./sh.mjs";
-import * as url from "url";
-
-const __dirname = url.fileURLToPath(new URL(".", import.meta.url)).slice(0, -1);
-
-const cmd = sh`flake8 perspective bench setup.py`;
-
-if (process.env.PSP_DOCKER) {
- cmd = sh`cd python/perspective`.sh(cmd);
- sh.docker(cmd).log().runSync();
-} else {
- const python_path = sh.path`${__dirname}/../../python/perspective`;
- cmd.cwd(python_path).log().runSync();
-}
diff --git a/tools/perspective-scripts/repack_wheel.mjs b/tools/perspective-scripts/repack_wheel.mjs
index 62e5b8ce31..2229a39429 100644
--- a/tools/perspective-scripts/repack_wheel.mjs
+++ b/tools/perspective-scripts/repack_wheel.mjs
@@ -12,14 +12,20 @@
import { execSync } from "node:child_process";
import * as fs from "node:fs";
+import * as path from "node:path";
+import pkg from "../../package.json" assert { type: "json" };
+
+const __dirname = new URL(".", import.meta.url).pathname;
const wheel_file = fs.readdirSync(".").filter((x) => x.endsWith(".whl"))[0];
const pkg_name = wheel_file.split("-").slice(0, 2).join("-");
execSync(`wheel unpack ${wheel_file}`);
fs.cpSync(
- "rust/perspective-python/perspective.data",
- `${pkg_name}/perspective.data`,
+ path.join(__dirname, "../../rust/perspective-python/perspective.data"),
+ `${pkg_name}/perspective_python-${pkg.version.replace(/-rc\.\d+/, (x) =>
+ x.replace("-", "").replace(".", "")
+ )}.data`,
{ recursive: true }
);