Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion nixtla/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
__version__ = "0.7.2"
__version__ = "0.7.3"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
__version__ = "0.7.3"
from importlib.metadata import version
__version__ = version("nixtla")

I suggest that in the near future, we standardize the naming of version similar to what we do in the other repositories like https://github.com/Nixtla/datasetsforecast/pull/83/changes

We also need to revise pyproject.toml file

__all__ = ["NixtlaClient"]
from .nixtla_client import NixtlaClient
40 changes: 29 additions & 11 deletions nixtla/scripts/snowflake_install_nixtla.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,33 +582,46 @@ def detect_package_installer() -> tuple[list[str], bool]:
)


def package_and_upload_nixtla(session: Session, stage: str) -> None:
def package_and_upload_nixtla(
session: Session, stage: str, fallback_package_source: Optional[str] = None
) -> None:
"""
Package nixtla client and upload to Snowflake stage.

Args:
session: Active Snowflake session
stage: Stage name to upload to
fallback_package_source: Local path to nixtla package (e.g. project root)
used as a fallback when the current version is not yet on PyPI.
"""
with TemporaryDirectory() as tmpdir:
# Import version from nixtla package
from nixtla import __version__ as nixtla_version

# Detect package installer
pip_cmd, use_uv = detect_package_installer()

# Install packages with appropriate flags
# UV uses --target instead of -t
install_args = pip_cmd + [
# Build base install args (shared between PyPI and fallback attempts)
base_args = pip_cmd + [
"--target" if use_uv else "-t",
tmpdir,
f"nixtla=={nixtla_version}",
"utilsforecast",
"httpx",
"--no-deps", # Avoid pulling in heavy things like pandas/numpy into the ZIP
]
extra_deps = ["utilsforecast", "httpx"]
no_deps_flag = ["--no-deps"] # Avoid pulling in heavy things like pandas/numpy

subprocess.run(install_args, check=True)
# Try the released PyPI version first
pypi_args = base_args + [f"nixtla=={nixtla_version}"] + extra_deps + no_deps_flag
pip_result = subprocess.run(pypi_args)

if pip_result.returncode != 0 and fallback_package_source is not None:
print(
f"[yellow]nixtla=={nixtla_version} not found on PyPI, "
f"falling back to local package: {fallback_package_source}[/yellow]"
)
fallback_args = base_args + [fallback_package_source] + extra_deps + no_deps_flag
subprocess.run(fallback_args, check=True)
elif pip_result.returncode != 0:
# No fallback available — surface the original error
pip_result.check_returncode()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
elif pip_result.returncode != 0:
# No fallback available — surface the original error
pip_result.check_returncode()
pip_result.check_returncode()

How about we always use check_returncode() regardless of the state of returncode? Per my understanding, it enforces "Does nothing if the return code is 0 (indicating success)"


# Create zip archive
shutil.make_archive(os.path.join(tmpdir, "nixtla"), "zip", tmpdir)
Expand Down Expand Up @@ -1865,6 +1878,7 @@ def deploy_snowflake_core(
deploy_procedures: bool = True,
deploy_finetune: bool = True,
deploy_examples: bool = True,
fallback_package_source: Optional[str] = None,
) -> DeploymentConfig:
"""
Core deployment logic without user interaction.
Expand All @@ -1883,6 +1897,8 @@ def deploy_snowflake_core(
deploy_procedures: Whether to create stored procedures
deploy_finetune: Whether to create finetune stored procedure
deploy_examples: Whether to load example datasets
fallback_package_source: Local path to nixtla package (e.g. project root)
used as a fallback when the current version is not yet on PyPI.

Returns:
DeploymentConfig that was used for deployment
Expand All @@ -1895,7 +1911,9 @@ def deploy_snowflake_core(
create_security_integration(session, config, api_key, skip_confirmation=True)

if deploy_package:
package_and_upload_nixtla(session, config.stage)
package_and_upload_nixtla(
session, config.stage, fallback_package_source=fallback_package_source
)

if deploy_udtfs:
create_udtfs(session, config)
Expand Down
7 changes: 7 additions & 0 deletions nixtla_tests/snowflake/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@
import os
import uuid
from dataclasses import dataclass
from pathlib import Path
from typing import Generator

import pandas as pd
import pytest

# Resolve the project root (where pyproject.toml lives) so tests install
# the local package instead of fetching an (unreleased) version from PyPI.
_PROJECT_ROOT = str(Path(__file__).resolve().parents[2])
from dotenv import load_dotenv
from snowflake.snowpark import Session

Expand Down Expand Up @@ -338,6 +343,7 @@ def deployed_with_api_endpoint(
deploy_procedures=True,
deploy_finetune=False, # Skip finetune to speed up tests
deploy_examples=False, # Load examples separately to get DataFrames
fallback_package_source=_PROJECT_ROOT,
)

yield config
Expand Down Expand Up @@ -396,6 +402,7 @@ def deployed_with_tsmp_endpoint(
deploy_procedures=True,
deploy_finetune=False, # Skip finetune to speed up tests
deploy_examples=False, # Load examples separately if needed
fallback_package_source=_PROJECT_ROOT,
)

yield config
Expand Down