Compare commits

...

2 Commits

Author SHA1 Message Date
dceoy 8da5ee9242 Bump version to v0.9.7
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-25 14:10:23 +09:00
Daichi Narushima 9dbb46fbb1 feat: make pyarrow optional via mt5cli[parquet] extra (#69) 2026-06-25 14:03:24 +09:00
7 changed files with 70 additions and 5 deletions
+6
View File
@@ -29,6 +29,12 @@ Built on top of [pdmt5](https://github.com/dceoy/pdmt5), a pandas-based data han
pip install -U mt5cli MetaTrader5 pip install -U mt5cli MetaTrader5
``` ```
Parquet export is not included by default. To enable it, install the `parquet` extra:
```bash
pip install -U "mt5cli[parquet]" MetaTrader5
```
## Python API (downstream packages) ## Python API (downstream packages)
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives. Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
+6
View File
@@ -27,6 +27,12 @@ mt5cli provides a stable `MT5Client` Python API, standardized dataset schemas, s
pip install mt5cli pip install mt5cli
``` ```
Parquet export is not included by default. To enable it, install the `parquet` extra:
```bash
pip install "mt5cli[parquet]"
```
## Python API for downstream packages ## Python API for downstream packages
Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives. Import `MT5Client` for generic MT5 data access, schema normalization, and optional order primitives.
+9
View File
@@ -314,6 +314,7 @@ def export_dataframe(
table_name: Table name for SQLite3 output. table_name: Table name for SQLite3 output.
Raises: Raises:
ImportError: If the parquet format is requested but pyarrow is not installed.
ValueError: If the output format is not supported. ValueError: If the output format is not supported.
""" """
if output_format == "csv": if output_format == "csv":
@@ -326,6 +327,14 @@ def export_dataframe(
indent=2, indent=2,
) )
elif output_format == "parquet": elif output_format == "parquet":
try:
__import__("pyarrow")
except ImportError as exc:
msg = (
"Parquet export requires the optional dependency pyarrow. "
'Install it with: pip install "mt5cli[parquet]"'
)
raise ImportError(msg) from exc
df.to_parquet(output_path, index=False) df.to_parquet(output_path, index=False)
elif output_format == "sqlite3": elif output_format == "sqlite3":
export_dataframe_to_sqlite( export_dataframe_to_sqlite(
+5 -2
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "mt5cli" name = "mt5cli"
version = "0.9.6" version = "0.9.7"
description = "Generic MT5 data and execution infrastructure for Python applications" description = "Generic MT5 data and execution infrastructure for Python applications"
authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}] authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}] maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
@@ -11,7 +11,6 @@ requires-python = ">= 3.11, < 3.14"
dependencies = [ dependencies = [
"pdmt5>=0.3.0", "pdmt5>=0.3.0",
"click >= 8.1.0", "click >= 8.1.0",
"pyarrow >= 19.0.0",
"typer >= 0.15.0", "typer >= 0.15.0",
] ]
classifiers = [ classifiers = [
@@ -25,6 +24,9 @@ classifiers = [
"Topic :: Office/Business :: Financial :: Investment", "Topic :: Office/Business :: Financial :: Investment",
] ]
[project.optional-dependencies]
parquet = ["pyarrow >= 19.0.0"]
[project.scripts] [project.scripts]
mt5cli = "mt5cli.cli:main" mt5cli = "mt5cli.cli:main"
@@ -42,6 +44,7 @@ dev = [
"pytest-mock >= 3.12.0", "pytest-mock >= 3.12.0",
"pytest-cov >= 5.0.0", "pytest-cov >= 5.0.0",
"pandas-stubs >= 2.2.3.250527", "pandas-stubs >= 2.2.3.250527",
"pyarrow >= 19.0.0",
"mkdocs >= 1.6.1", "mkdocs >= 1.6.1",
"mkdocs-material >= 9.7.6", "mkdocs-material >= 9.7.6",
"mkdocstrings[python] >= 1.0.4", "mkdocstrings[python] >= 1.0.4",
+22
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import re import re
import sqlite3 import sqlite3
from datetime import UTC, datetime from datetime import UTC, datetime
from importlib.metadata import requires
from pathlib import Path from pathlib import Path
from typing import get_type_hints from typing import get_type_hints
from unittest.mock import MagicMock from unittest.mock import MagicMock
@@ -832,3 +833,24 @@ class TestStableSdkContract:
assert result.index.tz is not None assert result.index.tz is not None
assert "time" not in result.columns assert "time" not in result.columns
assert "close" in result.columns assert "close" in result.columns
# ---------------------------------------------------------------------------
# Packaging metadata
# ---------------------------------------------------------------------------
def test_parquet_extra_declares_pyarrow() -> None:
"""Package metadata lists pyarrow under the parquet optional extra."""
reqs = requires("mt5cli") or []
parquet_reqs = [r for r in reqs if "pyarrow" in r and "parquet" in r]
assert parquet_reqs, "pyarrow not found in parquet optional extra"
def test_pyarrow_not_in_core_dependencies() -> None:
"""Pyarrow is not a core dependency; it belongs only in the parquet extra."""
reqs = requires("mt5cli") or []
core_reqs = [r for r in reqs if "extra ==" not in r]
assert not any("pyarrow" in r for r in core_reqs), (
"pyarrow should not appear in core dependencies"
)
+12
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import json import json
import sqlite3 import sqlite3
import sys
from datetime import UTC, datetime from datetime import UTC, datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@@ -111,6 +112,17 @@ class TestExportDataframe:
result = pd.read_parquet(output) result = pd.read_parquet(output)
pd.testing.assert_frame_equal(result, sample_df) pd.testing.assert_frame_equal(result, sample_df)
def test_export_parquet_without_pyarrow(
self,
tmp_path: Path,
sample_df: pd.DataFrame,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test that a clear error is raised when pyarrow is not installed."""
monkeypatch.setitem(sys.modules, "pyarrow", None)
with pytest.raises(ImportError, match="mt5cli\\[parquet\\]"):
export_dataframe(sample_df, tmp_path / "out.parquet", "parquet")
def test_export_sqlite3(self, tmp_path: Path, sample_df: pd.DataFrame) -> None: def test_export_sqlite3(self, tmp_path: Path, sample_df: pd.DataFrame) -> None:
"""Test SQLite3 export.""" """Test SQLite3 export."""
output = tmp_path / "out.db" output = tmp_path / "out.db"
Generated
+10 -3
View File
@@ -487,21 +487,26 @@ wheels = [
[[package]] [[package]]
name = "mt5cli" name = "mt5cli"
version = "0.9.6" version = "0.9.7"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "pdmt5" }, { name = "pdmt5" },
{ name = "pyarrow" },
{ name = "typer" }, { name = "typer" },
] ]
[package.optional-dependencies]
parquet = [
{ name = "pyarrow" },
]
[package.dev-dependencies] [package.dev-dependencies]
dev = [ dev = [
{ name = "mkdocs" }, { name = "mkdocs" },
{ name = "mkdocs-material" }, { name = "mkdocs-material" },
{ name = "mkdocstrings", extra = ["python"] }, { name = "mkdocstrings", extra = ["python"] },
{ name = "pandas-stubs" }, { name = "pandas-stubs" },
{ name = "pyarrow" },
{ name = "pymdown-extensions" }, { name = "pymdown-extensions" },
{ name = "pyright" }, { name = "pyright" },
{ name = "pytest" }, { name = "pytest" },
@@ -514,9 +519,10 @@ dev = [
requires-dist = [ requires-dist = [
{ name = "click", specifier = ">=8.1.0" }, { name = "click", specifier = ">=8.1.0" },
{ name = "pdmt5", specifier = ">=0.3.0" }, { name = "pdmt5", specifier = ">=0.3.0" },
{ name = "pyarrow", specifier = ">=19.0.0" }, { name = "pyarrow", marker = "extra == 'parquet'", specifier = ">=19.0.0" },
{ name = "typer", specifier = ">=0.15.0" }, { name = "typer", specifier = ">=0.15.0" },
] ]
provides-extras = ["parquet"]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
@@ -524,6 +530,7 @@ dev = [
{ name = "mkdocs-material", specifier = ">=9.7.6" }, { name = "mkdocs-material", specifier = ">=9.7.6" },
{ name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.4" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.4" },
{ name = "pandas-stubs", specifier = ">=2.2.3.250527" }, { name = "pandas-stubs", specifier = ">=2.2.3.250527" },
{ name = "pyarrow", specifier = ">=19.0.0" },
{ name = "pymdown-extensions", specifier = ">=10.21.2" }, { name = "pymdown-extensions", specifier = ">=10.21.2" },
{ name = "pyright", specifier = ">=1.1.407" }, { name = "pyright", specifier = ">=1.1.407" },
{ name = "pytest", specifier = ">=9.0.3" }, { name = "pytest", specifier = ">=9.0.3" },