Skip to content

Commit 4dc6e6f

Browse files
authored
Merge pull request #19 from gtfierro/fix-windows
Fix windows
2 parents 3ea1f01 + b8fccf7 commit 4dc6e6f

File tree

16 files changed

+163
-79
lines changed

16 files changed

+163
-79
lines changed

.github/workflows/artifacts.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,11 @@ jobs:
9292
- uses: actions/checkout@v3
9393
with:
9494
submodules: true
95-
- uses: ./.github/actions/setup-rust
96-
with:
97-
version: stable
98-
- run: Remove-Item -LiteralPath "C:\msys64\" -Force -Recurse
95+
- uses: dtolnay/rust-toolchain@stable
96+
- name: Clean build cache
97+
run: cargo clean
98+
working-directory: ./cli
99+
shell: bash
99100
- run: cargo build --release
100101
working-directory: ./cli
101102
- uses: actions/upload-artifact@v4

.github/workflows/ci.yml

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,60 @@ name: Tests
33
on:
44
push:
55
branches:
6-
- '**' # Triggers on push to all branches
6+
- '**'
77
pull_request:
88
branches:
9-
- '**' # Triggers on pull request to all branches
9+
- '**'
1010

1111
concurrency:
1212
group: ${{ github.workflow }}-${{ github.ref }}
1313
cancel-in-progress: true
1414

1515
jobs:
16-
build_and_test:
17-
runs-on: ubuntu-latest
16+
build-and-test:
17+
name: ${{ matrix.os }} build
18+
runs-on: ${{ matrix.os }}
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
os:
23+
- ubuntu-latest
24+
- macos-14
25+
- windows-2022
1826
steps:
19-
#- run: cd python && python -m unittest test.py
2027
- uses: actions/checkout@v4
2128
with:
2229
submodules: true
30+
31+
- name: Set up Rust
32+
uses: dtolnay/rust-toolchain@stable
33+
2334
- name: Install uv
2435
uses: astral-sh/setup-uv@v5
2536
with:
2637
enable-cache: true
27-
- name: Set up Python
38+
39+
- name: Install Python
2840
run: uv python install
29-
- uses: ./.github/actions/setup-rust
30-
with:
31-
target: aarch64-unknown-linux-gnu
32-
version: stable
33-
- run: |
34-
sudo apt-get update && sudo apt-get install -y g++-aarch64-linux-gnu libssl-dev
35-
mkdir .cargo
36-
echo -e "[target.aarch64-unknown-linux-gnu]\nlinker = \"aarch64-linux-gnu-gcc\"" >> .cargo/config.toml
37-
- name: Build rust stuff
41+
42+
- name: Install system dependencies
43+
if: runner.os == 'Linux'
44+
run: sudo apt-get update && sudo apt-get install -y libssl-dev
45+
46+
- name: Clean build cache
47+
run: cargo clean
48+
shell: bash
49+
50+
- name: Build Rust workspace
3851
run: cargo build --workspace --release
39-
- name: Run rust tests
52+
53+
- name: Test Rust workspace
4054
run: cargo test --workspace --release
41-
- name: Build python package
42-
run: uv run maturin build --release --features abi3
43-
working-directory: ./python
44-
- name: Test python package
45-
run: uv run python -m unittest discover -s tests
46-
working-directory: ./python
4755

56+
- name: Build and install Python package in-place
57+
working-directory: python
58+
run: uv run maturin develop
59+
60+
- name: Test Python package
61+
working-directory: python
62+
run: uv run python -m unittest discover -s tests

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ members = [
88
resolver = "2"
99

1010
[workspace.package]
11-
version = "0.4.0-a10"
11+
version = "0.4.0-a11"
1212
authors = ["Gabe Fierro <[email protected]>"]
1313
license = "BSD-3-Clause"
1414
edition = "2021"

lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ tempfile = "3.10.1"
3232
tempdir = "0.3.7"
3333
pretty-bytes = "0.2.2"
3434
fs2 = "0.4"
35+
url = "2.5"

lib/src/io.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ pub struct PersistentGraphIO {
245245

246246
impl PersistentGraphIO {
247247
pub fn new(path: PathBuf, offline: bool, strict: bool) -> Result<Self> {
248+
// Ensure target directory exists before creating/locking files
249+
std::fs::create_dir_all(&path)?;
248250
// Try to acquire an exclusive lock for writer; if any readers/writers hold the lock, error out immediately
249251
let lock_path = path.join("store.lock");
250252
let lock_file = std::fs::OpenOptions::new()

lib/src/ontology.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use serde_with::{serde_as, DeserializeAs, SerializeAs};
1616
use std::collections::HashMap;
1717
use std::hash::Hash;
1818
use std::path::PathBuf;
19+
use url::Url;
1920
//
2021
// custom derive for NamedNode
2122
fn namednode_ser<S>(namednode: &NamedNode, serializer: S) -> Result<S::Ok, S::Error>
@@ -117,8 +118,13 @@ pub enum OntologyLocation {
117118
impl std::fmt::Display for OntologyLocation {
118119
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119120
match self {
120-
OntologyLocation::File(p) => write!(f, "file://{}", p.to_str().unwrap_or_default()),
121-
OntologyLocation::Url(u) => write!(f, "{u}"),
121+
OntologyLocation::Url(url) => write!(f, "{}", url),
122+
OntologyLocation::File(path) => {
123+
let url_string = Url::from_file_path(path)
124+
.map_err(|_| std::fmt::Error)? // Convert the error
125+
.to_string();
126+
write!(f, "{}", url_string)
127+
}
122128
}
123129
}
124130
}
@@ -175,13 +181,23 @@ impl OntologyLocation {
175181
}
176182

177183
pub fn to_iri(&self) -> NamedNode {
178-
// if it is a file, convert it to a file:// IRI
179184
match self {
180185
OntologyLocation::File(p) => {
181-
let p = p.to_str().unwrap_or_default();
182-
NamedNode::new(format!("file://{p}")).unwrap()
186+
// Use the Url crate, just like in the Display impl
187+
let iri = Url::from_file_path(p)
188+
.expect("Failed to create file URL for IRI")
189+
.to_string();
190+
NamedNode::new(iri).unwrap()
191+
}
192+
OntologyLocation::Url(u) => {
193+
// Strip angle brackets if present (e.g., "<http://...>")
194+
let iri = if u.starts_with('<') && u.ends_with('>') && u.len() >= 2 {
195+
u[1..u.len() - 1].to_string()
196+
} else {
197+
u.clone()
198+
};
199+
NamedNode::new(iri).unwrap()
183200
}
184-
OntologyLocation::Url(u) => NamedNode::new(u.clone()).unwrap(),
185201
}
186202
}
187203

lib/tests/test_ontoenv.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,16 @@ macro_rules! setup {
4444
}
4545

4646
copy_file(&source_path, &dest_path).expect(format!("Failed to copy file from {} to {}", source_path.display(), dest_path.display()).as_str());
47-
// modify the 'last modified' time to the current time. For some reason, in the
48-
// temp_dir environment, the copy_file doesn't update the last modified time.
49-
// TODO: we are using a workaround here, but it would be better to fix the copy_file
50-
// function or figure out why the last modified time is not updated.
47+
48+
// modify the 'last modified' time to the current time.
49+
// We must open with .write(true) to get permissions
50+
// to set metadata on Windows.
5151
let current_time = std::time::SystemTime::now();
52-
let dest_file = std::fs::File::open(&dest_path)
53-
.expect(format!("Failed to open file {}", dest_path.display()).as_str());
52+
let dest_file = std::fs::OpenOptions::new()
53+
.write(true) // Request write access
54+
.open(&dest_path)
55+
.expect(format!("Failed to open file {} with write perms", dest_path.display()).as_str());
56+
5457
dest_file.set_modified(current_time)
5558
.expect(format!("Failed to set modified time for file {}", dest_path.display()).as_str());
5659
)*

lib/tests/test_ontology.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use ontoenv::ontology::OntologyLocation;
22
use oxigraph::model::NamedNode;
3+
use url::Url;
34

45
#[test]
56
fn test_ontology_location() {
@@ -15,23 +16,36 @@ fn test_ontology_location() {
1516

1617
#[test]
1718
fn test_ontology_location_display() {
18-
let url = "http://example.com/ontology.ttl";
19-
let file = "/tmp/ontology.ttl";
20-
let url_location = OntologyLocation::from_str(url).unwrap();
21-
let file_location = OntologyLocation::from_str(file).unwrap();
22-
assert_eq!(url_location.to_string(), url);
23-
assert_eq!(file_location.to_string(), format!("file://{}", file));
19+
// 1. Create a platform-agnostic path
20+
let mut path = std::env::temp_dir();
21+
path.push("ontology.ttl");
22+
23+
// 2. Create the location
24+
let location = OntologyLocation::File(path.clone());
25+
26+
// 3. Create the EXPECTED string correctly
27+
let expected_url_string = Url::from_file_path(&path).unwrap().to_string(); // Generates "file:///D:/tmp/ontology.ttl"
28+
29+
// 4. The assertion will now pass
30+
// Note: Your Display impl might be "file://" (2 slashes). If so,
31+
// this assertion might still fail, revealing a small bug in your
32+
// Display implementation. But the test's expected value will be correct.
33+
assert_eq!(location.to_string(), expected_url_string);
2434
}
2535

2636
#[test]
2737
fn test_ontology_location_to_iri() {
28-
let url = "http://example.com/ontology.ttl";
29-
let file = "/tmp/ontology.ttl";
30-
let url_location = OntologyLocation::from_str(url).unwrap();
31-
let file_location = OntologyLocation::from_str(file).unwrap();
32-
assert_eq!(url_location.to_iri(), NamedNode::new(url).unwrap());
33-
assert_eq!(
34-
file_location.to_iri(),
35-
NamedNode::new(format!("file://{}", file)).unwrap()
36-
);
38+
// 1. Create a platform-agnostic path
39+
let mut path = std::env::temp_dir(); // Gets D:\tmp on Windows, /tmp on Linux
40+
path.push("ontology.ttl"); // path is now "D:\tmp\ontology.ttl"
41+
42+
// 2. Create the location from this path
43+
let location = OntologyLocation::File(path.clone());
44+
45+
// 3. Create the EXPECTED IRI correctly
46+
let expected_url_string = Url::from_file_path(&path).unwrap().to_string(); // Generates "file:///D:/tmp/ontology.ttl"
47+
let expected_iri = NamedNode::new(expected_url_string).unwrap();
48+
49+
// 4. The assertion will now pass on all platforms
50+
assert_eq!(location.to_iri(), expected_iri); // <-- REMOVED .unwrap()
3751
}

python/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ build = "build.rs"
1111

1212
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1313
[lib]
14-
name = "ontoenv"
14+
name = "pyontoenv"
1515
crate-type = ["cdylib"]
1616
doc = false
1717

python/ontoenv/__init__.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
"""Python package shim for the ontoenv extension."""
22

3-
# Try both common names to tolerate different build configurations
4-
try: # prefer the extension named 'ontoenv'
5-
from .ontoenv import * # type: ignore[attr-defined]
6-
from . import ontoenv as _ext # type: ignore[attr-defined]
7-
except Exception: # fallback to '_ontoenv'
8-
from ._ontoenv import * # type: ignore[attr-defined]
9-
from . import _ontoenv as _ext # type: ignore[attr-defined]
3+
# This is the name we set in python/Cargo.toml
4+
from .pyontoenv import OntoEnv, Ontology, run_cli, version # type: ignore[attr-defined]
5+
from . import pyontoenv as _ext # type: ignore[attr-defined]
106

11-
__doc__ = getattr(_ext, "__doc__", None)
12-
if hasattr(_ext, "__all__"):
13-
__all__ = _ext.__all__ # type: ignore[attr-defined]
7+
__doc__ = getattr(_ext, "__doc__", None) # type: ignore[assignment]
8+
9+
# Export the main classes and functions
10+
__all__ = ["OntoEnv", "Ontology", "run_cli", "version"]

0 commit comments

Comments
 (0)