Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions python/sedonadb/python/sedonadb/_scarf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Scarf analytics utility functions."""

import os
import platform
import threading
import urllib.request


def make_scarf_call(language: str) -> None:
"""Make a call to Scarf for usage analytics.

Args:
language: The language identifier (e.g., 'python', 'adbc', 'dbapi')
"""

def _scarf_request():
try:
# Check for user opt-out
if (
os.environ.get("SCARF_NO_ANALYTICS") is not None
or os.environ.get("DO_NOT_TRACK") is not None
):
return

# Detect architecture and OS
arch = platform.machine().lower().replace(" ", "_")
os_name = platform.system().lower().replace(" ", "_")

# Construct Scarf URL
scarf_url = (
f"https://sedona.gateway.scarf.sh/sedona-db/{arch}/{os_name}/{language}"
)

# Make the request in a non-blocking way
urllib.request.urlopen(scarf_url, timeout=1)
except Exception:
# Silently ignore any errors - we don't want Scarf calls to break user code
pass

# Run in a separate thread to avoid blocking
thread = threading.Thread(target=_scarf_request, daemon=True)
thread.start()
4 changes: 4 additions & 0 deletions python/sedonadb/python/sedonadb/adbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@

def connect() -> adbc_driver_manager.AdbcDatabase:
"""Create a low level ADBC connection to Sedona."""
# Make Scarf call for usage analytics
from ._scarf import make_scarf_call

make_scarf_call("adbc")
return adbc_driver_manager.AdbcDatabase(
driver=_lib.__file__, entrypoint="AdbcSedonadbDriverInit"
)
4 changes: 4 additions & 0 deletions python/sedonadb/python/sedonadb/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ def sql(self, sql: str) -> DataFrame:

def connect() -> SedonaContext:
"""Create a new [SedonaContext][sedonadb.context.SedonaContext]"""
# Make Scarf call for usage analytics
from ._scarf import make_scarf_call

make_scarf_call("python")
return SedonaContext()


Expand Down
5 changes: 5 additions & 0 deletions python/sedonadb/python/sedonadb/dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

def connect(**kwargs) -> "Connection":
"""Connect to Sedona via ADBC."""
# Make Scarf call for usage analytics
from ._scarf import make_scarf_call

make_scarf_call("dbapi")

db = None
conn = None

Expand Down
6 changes: 6 additions & 0 deletions rust/sedona/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ pub struct SedonaContext {
impl SedonaContext {
/// Creates a new context with default options
pub fn new() -> Self {
// Make Scarf call for usage analytics
crate::scarf::make_scarf_call("rust");

// This will panic only if the default build settings are
// incorrect which we test!
Self::new_from_context(SessionContext::new()).unwrap()
Expand All @@ -77,6 +80,9 @@ impl SedonaContext {
/// Initializes a context from the current environment and registers access
/// to the local file system.
pub async fn new_local_interactive() -> Result<Self> {
// Make Scarf call for usage analytics
crate::scarf::make_scarf_call("rust");

// These three objects enable configuring various elements of the runtime.
// Eventually we probably want to have a common set of configuration parameters
// exposed via the CLI/Python as arguments, via ADBC as connection options,
Expand Down
1 change: 1 addition & 0 deletions rust/sedona/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ mod object_storage;
pub mod random_geometry_provider;
pub mod reader;
pub mod record_batch_reader_provider;
pub mod scarf;
pub mod show;
63 changes: 63 additions & 0 deletions rust/sedona/src/scarf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

/// Make a call to Scarf for usage analytics.
///
/// # Arguments
///
/// * `language` - The language identifier (e.g., "rust", "cli")
pub fn make_scarf_call(language: &str) {
let language = language.to_string();
std::thread::spawn(move || {
let _ = scarf_request(&language);
});
}

fn scarf_request(language: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Check for user opt-out
if std::env::var("SCARF_NO_ANALYTICS").is_ok() || std::env::var("DO_NOT_TRACK").is_ok() {
return Ok(());
}

// Detect architecture and OS
let arch = std::env::consts::ARCH.to_lowercase().replace(' ', "_");
let os = std::env::consts::OS.to_lowercase().replace(' ', "_");

// Construct Scarf URL
let scarf_url = format!("https://sedona.gateway.scarf.sh/sedona-db/{arch}/{os}/{language}");

// Make the request using std::net::TcpStream for a simple HTTP GET
if let Ok(url) = url::Url::parse(&scarf_url) {
if let Some(host) = url.host_str() {
let port = url.port().unwrap_or(443);
let path = url.path();

// Try to make a simple HTTP request
if let Ok(addr) = format!("{host}:{port}").parse::<std::net::SocketAddr>() {
if let Ok(mut stream) =
std::net::TcpStream::connect_timeout(&addr, std::time::Duration::from_secs(1))
{
let request =
format!("GET {path} HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n");
let _ = std::io::Write::write_all(&mut stream, request.as_bytes());
}
}
}
}

Ok(())
}
3 changes: 3 additions & 0 deletions sedona-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ async fn main_inner() -> Result<()> {
println!("Sedona CLI v{DATAFUSION_CLI_VERSION}");
}

// Make Scarf call for usage analytics
sedona::scarf::make_scarf_call("cli");

if let Some(ref path) = args.data_path {
let p = Path::new(path);
env::set_current_dir(p).unwrap();
Expand Down