From e7d92abef4e3dfbe9ee69f52d24f89d2ae7c1eca Mon Sep 17 00:00:00 2001 From: Andrew Stein Date: Sun, 28 Jul 2024 21:12:49 -0400 Subject: [PATCH 1/3] Add sdist, lint Signed-off-by: Andrew Stein --- .github/actions/install-deps/action.yaml | 15 +- .github/workflows/build.yaml | 27 +- README.md | 2 +- docs/docs/development.md | 77 +-- docs/docs/js.md | 133 +--- docs/docs/python.md | 1 - examples/rust-axum/src/main.rs | 21 +- package.json | 2 +- rust/perspective-client/docs/client.md | 4 +- .../docs/client/system_info.md | 2 +- rust/perspective-client/docs/client/table.md | 4 +- .../docs/table/get_client.md | 1 + .../docs/table/get_features.md | 2 + .../docs/view/get_config.md | 2 +- rust/perspective-client/src/rust/view.rs | 6 +- rust/perspective-js/package.json | 3 +- rust/perspective-python/Cargo.toml | 1 - rust/perspective-python/build.mjs | 12 +- .../perspective/__init__.py | 2 +- .../perspective/widget/widget.py | 8 +- rust/perspective-python/pyproject.toml | 2 +- rust/perspective-viewer/docs.js | 74 --- rust/perspective-viewer/exprtk.md | 623 ------------------ rust/perspective-viewer/package.json | 5 - .../src/rust/components/containers/sidebar.rs | 3 +- tools/perspective-scripts/docs.mjs | 29 - tools/perspective-scripts/fix.mjs | 3 +- tools/perspective-scripts/fix_python.mjs | 26 - tools/perspective-scripts/lint.mjs | 13 +- tools/perspective-scripts/lint_cpp.mjs | 16 +- tools/perspective-scripts/lint_python.mjs | 26 - 31 files changed, 132 insertions(+), 1013 deletions(-) delete mode 100644 rust/perspective-viewer/docs.js delete mode 100644 rust/perspective-viewer/exprtk.md delete mode 100644 tools/perspective-scripts/docs.mjs delete mode 100644 tools/perspective-scripts/fix_python.mjs delete mode 100644 tools/perspective-scripts/lint_python.mjs 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..4ac4ae8d4d 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 +[Playwright](https://playwright.dev/) 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: - ```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/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..73df9fde22 100644 --- a/rust/perspective-python/perspective/__init__.py +++ b/rust/perspective-python/perspective/__init__.py @@ -73,6 +73,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/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..c8e974142b 100644 --- a/tools/perspective-scripts/lint_cpp.mjs +++ b/tools/perspective-scripts/lint_cpp.mjs @@ -63,12 +63,20 @@ function tidy(buildDir, sourceDir, flags) { } const CLANG_TIDY = `run-clang-tidy`; -const CLANG_FORMAT = `clang-format`; +const CLANG_FORMAT = fs.existsSync(`${__dirname}../../.llvm/bin/clang-format`) + ? `${__dirname}../../.llvm/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(); -} From ca5cc0d27ccb9696301c360c6c3fd340afd14cd6 Mon Sep 17 00:00:00 2001 From: No Author <> Date: Sun, 28 Jul 2024 21:14:00 -0400 Subject: [PATCH 2/3] Apply lint Signed-off-by: Andrew Stein --- docs/docs/development.md | 4 +- .../bench/tornado/server/new_api.py | 2 - .../perspective/__init__.py | 1 + .../perspective/handlers/__init__.py | 6 +- .../perspective/tests/conftest.py | 44 +-- .../perspective/tests/core/test_async.py | 25 +- .../perspective/tests/server/test_server.py | 6 +- .../perspective/tests/table/test_table.py | 10 +- .../tests/table/test_table_datetime.py | 2 +- .../tests/table/test_table_infer.py | 2 +- .../tests/table/test_table_limit.py | 2 - .../perspective/tests/table/test_to_arrow.py | 5 +- .../tests/table/test_to_arrow_lz4.py | 1 - .../tests/table/test_update_numpy.py | 252 ------------------ .../perspective/tests/table/test_view.py | 1 - .../tests/table/test_view_expression.py | 8 +- .../perspective/tests/viewer/test_validate.py | 1 - tools/perspective-scripts/lint_cpp.mjs | 6 +- 18 files changed, 52 insertions(+), 326 deletions(-) delete mode 100644 rust/perspective-python/perspective/tests/table/test_update_numpy.py diff --git a/docs/docs/development.md b/docs/docs/development.md index 4ac4ae8d4d..c449bd7b4d 100644 --- a/docs/docs/development.md +++ b/docs/docs/development.md @@ -199,8 +199,8 @@ pnpm run test 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 -[Playwright](https://playwright.dev/) tests, which -assert the behavior of the rest of the UI facing packages. +[Playwright](https://playwright.dev/) tests, which assert the behavior of the +rest of the UI facing packages. ```bash pnpm run test --update-snapshots 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/perspective/__init__.py b/rust/perspective-python/perspective/__init__.py index 73df9fde22..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", ] 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/tools/perspective-scripts/lint_cpp.mjs b/tools/perspective-scripts/lint_cpp.mjs index c8e974142b..bec8199c41 100644 --- a/tools/perspective-scripts/lint_cpp.mjs +++ b/tools/perspective-scripts/lint_cpp.mjs @@ -63,8 +63,10 @@ function tidy(buildDir, sourceDir, flags) { } const CLANG_TIDY = `run-clang-tidy`; -const CLANG_FORMAT = fs.existsSync(`${__dirname}../../.llvm/bin/clang-format`) - ? `${__dirname}../../.llvm/bin/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) { From 94b8f9a301cd334f6eceff9563e03ffdc7f1159b Mon Sep 17 00:00:00 2001 From: Tim Bess Date: Sat, 3 Aug 2024 20:48:40 -0400 Subject: [PATCH 3/3] Fix data directory name for qualified versions Signed-off-by: Andrew Stein --- tools/perspective-scripts/repack_wheel.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 } );