Skip to content

Commit 7f84f96

Browse files
authored
VER: Release 0.41.0
See release notes.
2 parents 899df69 + 701d745 commit 7f84f96

File tree

8 files changed

+125
-20
lines changed

8 files changed

+125
-20
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## 0.41.0 - 2024-09-03
4+
5+
#### Enhancements
6+
- Added `databento.read_dbn` alias
7+
- Added `mode` parameter to `DBNStore.to_file` to control the file writing mode
8+
9+
#### Breaking changes
10+
- Changed default write mode for `DBNStore.to_file` to overwrite ("w")
11+
12+
#### Deprecations
13+
- Deprecated `databento.from_dbn` and will be removed in a future release, use `databento.read_dbn` instead
14+
315
## 0.40.0 - 2024-08-27
416

517
#### Enhancements

databento/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from databento.common.publishers import Venue
4545
from databento.common.symbology import InstrumentMap
4646
from databento.common.types import DBNRecord
47+
from databento.common.validation import deprecated
4748
from databento.historical.client import Historical
4849
from databento.live.client import Live
4950
from databento.reference.client import Reference
@@ -109,6 +110,7 @@
109110

110111
# Convenience imports
111112
enable_logging = bentologging.enable_logging
112-
from_dbn = DBNStore.from_file
113+
from_dbn = deprecated("databento.from_dbn")(DBNStore.from_file)
114+
read_dbn = DBNStore.from_file
113115
map_symbols_csv = symbology.map_symbols_csv
114116
map_symbols_json = symbology.map_symbols_json

databento/common/dbnstore.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,14 +1029,20 @@ def to_parquet(
10291029
if writer is not None:
10301030
writer.close()
10311031

1032-
def to_file(self, path: PathLike[str] | str) -> None:
1032+
def to_file(
1033+
self,
1034+
path: PathLike[str] | str,
1035+
mode: Literal["w", "x"] = "w",
1036+
) -> None:
10331037
"""
10341038
Write the data to a DBN file at the given path.
10351039
10361040
Parameters
10371041
----------
10381042
path : PathLike[str] or str
10391043
The file path to write to.
1044+
mode : str, default "w"
1045+
The file write mode to use, either "x" or "w".
10401046
10411047
Raises
10421048
------
@@ -1048,9 +1054,8 @@ def to_file(self, path: PathLike[str] | str) -> None:
10481054
If path is not writable.
10491055
10501056
"""
1051-
file_path = validate_file_write_path(path, "path")
1052-
with open(file_path, mode="xb") as f:
1053-
f.write(self._data_source.reader.read())
1057+
file_path = validate_file_write_path(path, "path", exist_ok=mode == "w")
1058+
file_path.write_bytes(self._data_source.reader.read())
10541059
self._data_source = FileDataSource(file_path)
10551060

10561061
def to_json(

databento/common/validation.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
from __future__ import annotations
22

3+
import functools
34
import os
5+
import warnings
6+
from collections.abc import Callable
47
from enum import Enum
58
from os import PathLike
69
from pathlib import Path
10+
from typing import Any
711
from typing import TypeVar
812
from urllib.parse import urlsplit
913
from urllib.parse import urlunsplit
@@ -44,7 +48,11 @@ def validate_path(value: PathLike[str] | str, param: str) -> Path:
4448
) from None
4549

4650

47-
def validate_file_write_path(value: PathLike[str] | str, param: str) -> Path:
51+
def validate_file_write_path(
52+
value: PathLike[str] | str,
53+
param: str,
54+
exist_ok: bool = False,
55+
) -> Path:
4856
"""
4957
Validate whether the given value is a valid path to a writable file.
5058
@@ -54,6 +62,8 @@ def validate_file_write_path(value: PathLike[str] | str, param: str) -> Path:
5462
The value to validate.
5563
param : str
5664
The name of the parameter being validated (for any error message).
65+
exist_ok : bool, default False
66+
If False, raises a `FileExistsError` if the file exists.
5767
5868
Returns
5969
-------
@@ -75,7 +85,7 @@ def validate_file_write_path(value: PathLike[str] | str, param: str) -> Path:
7585
raise PermissionError(f"The file `{value}` is not writable.")
7686
if path_valid.is_dir():
7787
raise IsADirectoryError(f"The `{param}` was not a path to a file.")
78-
if path_valid.is_file():
88+
if not exist_ok and path_valid.is_file():
7989
raise FileExistsError(f"The file `{value}` already exists.")
8090
return path_valid
8191

@@ -262,3 +272,37 @@ def validate_smart_symbol(symbol: str) -> str:
262272
tokens[1] = tokens[1].lower() # api expects lower case
263273

264274
return ".".join(tokens)
275+
276+
277+
_D = TypeVar("_D", bound=Callable) # type: ignore [type-arg]
278+
279+
280+
def deprecated(name: str | None = None) -> Callable[[_D], _D]:
281+
"""
282+
Decorate for a function that will emit a deprecation warning.
283+
284+
Parameters
285+
----------
286+
name : str, optional
287+
An optional name to use instead of the actual function name.
288+
289+
Returns
290+
-------
291+
Callable[..., Any]
292+
293+
"""
294+
295+
def decorator(func: _D) -> _D:
296+
@functools.wraps(func)
297+
def wrapper(*args: Any, **kwargs: Any) -> Any:
298+
func_name = name if name is not None else func.__name__
299+
warnings.warn(
300+
f"{func_name} is deprecated and will be removed in a future release",
301+
category=DeprecationWarning,
302+
stacklevel=2,
303+
)
304+
return func(*args, **kwargs)
305+
306+
return wrapper # type: ignore
307+
308+
return decorator

databento/historical/api/batch.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
from databento.common.parsing import optional_values_list_to_string
4040
from databento.common.parsing import symbols_list_to_list
4141
from databento.common.publishers import Dataset
42-
from databento.common.types import Default
4342
from databento.common.validation import validate_enum
4443
from databento.common.validation import validate_path
4544
from databento.common.validation import validate_semantic_string
@@ -253,7 +252,6 @@ def download(
253252
job_id: str,
254253
output_dir: PathLike[str] | str | None = None,
255254
filename_to_download: str | None = None,
256-
enable_partial_downloads: Default[bool] = Default[bool](True),
257255
) -> list[Path]:
258256
"""
259257
Download a batch job or a specific file to `{output_dir}/{job_id}/`.
@@ -287,14 +285,6 @@ def download(
287285
If a file fails to download.
288286
289287
"""
290-
# TODO: Remove after a reasonable deprecation period
291-
if not isinstance(enable_partial_downloads, Default):
292-
warnings.warn(
293-
"The parameter `enable_partial_downloads` has been removed and will cause an error if set in the future. Partially downloaded files will always be resumed.",
294-
category=BentoWarning,
295-
stacklevel=2,
296-
)
297-
298288
if filename_to_download is None:
299289
filenames_to_download = None
300290
else:

databento/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.40.0"
1+
__version__ = "0.41.0"

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "databento"
3-
version = "0.40.0"
3+
version = "0.41.0"
44
description = "Official Python client library for Databento"
55
authors = [
66
"Databento <[email protected]>",

tests/test_historical_bento.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,23 +174,75 @@ def test_file_dbnstore_given_valid_path_initialized_expected_data(
174174
assert dbnstore.nbytes == 189
175175

176176

177+
@pytest.mark.parametrize(
178+
"schema,expected_size",
179+
[
180+
(Schema.MBO, 189),
181+
(Schema.DEFINITION, 290),
182+
],
183+
)
177184
def test_to_file_persists_to_disk(
178185
test_data: Callable[[Dataset, Schema], bytes],
179186
tmp_path: Path,
187+
schema: Schema,
188+
expected_size: int,
180189
) -> None:
190+
"""
191+
Test the DBNStore.to_file writes files to disk.
192+
"""
181193
# Arrange
182-
stub_data = test_data(Dataset.GLBX_MDP3, Schema.MBO)
194+
stub_data = test_data(Dataset.GLBX_MDP3, schema)
183195
dbnstore = DBNStore.from_bytes(data=stub_data)
184196

185197
# Act
186198
dbn_path = tmp_path / "my_test.dbn"
187199
dbnstore.to_file(path=dbn_path)
188200

201+
# Assert
202+
assert dbn_path.exists()
203+
assert dbn_path.stat().st_size == expected_size
204+
205+
206+
def test_to_file_overwrite(
207+
test_data: Callable[[Dataset, Schema], bytes],
208+
tmp_path: Path,
209+
) -> None:
210+
"""
211+
Test that the default write mode allows files to be overwritten.
212+
"""
213+
# Arrange
214+
stub_data = test_data(Dataset.GLBX_MDP3, Schema.MBO)
215+
dbnstore = DBNStore.from_bytes(data=stub_data)
216+
dbn_path = tmp_path / "my_test.dbn"
217+
dbnstore.to_file(path=dbn_path)
218+
assert dbn_path.stat().st_size == 189
219+
220+
# Act
221+
dbnstore.to_file(path=dbn_path)
222+
189223
# Assert
190224
assert dbn_path.exists()
191225
assert dbn_path.stat().st_size == 189
192226

193227

228+
def test_to_file_exclusive(
229+
test_data: Callable[[Dataset, Schema], bytes],
230+
tmp_path: Path,
231+
) -> None:
232+
"""
233+
Test that the exclusive write mode correctly rejects an existing file path.
234+
"""
235+
# Arrange
236+
stub_data = test_data(Dataset.GLBX_MDP3, Schema.MBO)
237+
dbnstore = DBNStore.from_bytes(data=stub_data)
238+
dbn_path = tmp_path / "my_test.dbn"
239+
dbnstore.to_file(path=dbn_path)
240+
241+
# Act, Assert
242+
with pytest.raises(FileExistsError):
243+
dbnstore.to_file(path=dbn_path, mode="x")
244+
245+
194246
def test_to_ndarray_with_stub_data_returns_expected_array(
195247
test_data: Callable[[Dataset, Schema], bytes],
196248
) -> None:

0 commit comments

Comments
 (0)