Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2c94542
low_res_datetime
Jan 29, 2025
e8f42ce
add tests
Jan 29, 2025
8808f2e
Move tests out of package tree
suvayu Apr 1, 2025
8daacea
Fix tests
suvayu Apr 1, 2025
b5854f1
reencode.py: fix to match model rename, and some refactor
suvayu Apr 1, 2025
aad2161
models.py: add bytes to support `Any` (flexible) data types
suvayu Apr 1, 2025
d5db0e6
dbmap.py: major refactor
suvayu Apr 1, 2025
edb587c
dbmap: fix _formatter type hints
suvayu Apr 4, 2025
235418d
dbmap: simplify append for single values
suvayu Apr 4, 2025
cc68533
dbmap: more robust handling of "index_type" for "map"s
suvayu Apr 4, 2025
e9c9540
reencode: clean up unused imports, undo index name default
suvayu Apr 4, 2025
05c93cc
dbmap: add docs, move tests
suvayu Apr 4, 2025
20da499
dbmap: fix how default index level names are handled
suvayu Apr 8, 2025
1c8e97c
dbmap: remove redundant value_type check from a case
suvayu Apr 8, 2025
c18f3ee
dbmap: fix index_name in deprecation warning
suvayu Apr 10, 2025
078428b
dbmap: simplify underlying formatter function signature for testing
suvayu Apr 10, 2025
8542e3a
dbmap: check index_name uniqueness, add assertion message
suvayu Apr 10, 2025
ab6f766
dbmap: format duration as pandas.DateOffset
suvayu Apr 10, 2025
21d5f95
{dbmap,models}.py: time_pattern using simple dataclass
suvayu Apr 10, 2025
54cbda2
dbmap: fix handling of array-like time_series
suvayu Apr 10, 2025
232cd7d
dbmap: bug fix to_dateoffset
suvayu Apr 10, 2025
1fac7cd
dbmap: remove redundant "renaming" of time/period column
suvayu Apr 10, 2025
0c05497
tests: sample JSON file was incorrect
suvayu Apr 10, 2025
d39af2d
dbmap: two bug fixes
suvayu Apr 11, 2025
a583807
models.py: simplify bytes array
suvayu Apr 11, 2025
1edb2a8
models.py: add more type maps
suvayu Apr 11, 2025
6b34e62
models: add alternate TimePattern w/o Pydantic
suvayu Apr 11, 2025
b193d74
dbmap: rename internal functions as `_<myfunc>`
suvayu Apr 11, 2025
2b5d06c
tests/test_dbmap: add and update tests
suvayu Apr 11, 2025
ade6509
tests/test_integration: refactor for numpy array comparison
suvayu Apr 11, 2025
91d920b
dbmap: refactor for simpler & more consistent use of datetime
suvayu Apr 12, 2025
3bc1325
tests: add tests for maps
suvayu Apr 12, 2025
c0e0972
dbmap: anchor SEQ_PAT regex, add tests
suvayu Apr 12, 2025
0f904d1
Add CI
suvayu Apr 12, 2025
9654341
fix linting
suvayu Apr 12, 2025
8050c0a
fix formatting
suvayu Apr 12, 2025
356dd1e
models: minor platform dependent fix
suvayu Apr 12, 2025
5a92848
tests: add missing conftest.py
suvayu Apr 12, 2025
9417bae
ci: run prerelease only on linux
suvayu Apr 12, 2025
6065392
ci: disable testing on prerelease because not all wheels aren't avl
suvayu Apr 12, 2025
92febb7
reencode: fix types
suvayu May 1, 2025
921dbc8
reencode: more precise types and robust pattern matching
suvayu May 1, 2025
7f22c5e
Fix integration tests (only for "numbers")
suvayu May 1, 2025
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
105 changes: 105 additions & 0 deletions .github/workflows/unittests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Unit tests

on:
push:
paths-ignore:
- "docs/**"
- "*.md"
pull_request:
paths-ignore:
- "docs/**"
- "*.md"

jobs:
format:
if: |
(github.event_name == 'push' || github.event.pull_request.head.repo.fork) &&
(!contains(github.event.head_commit.message, 'skip ci'))

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: 3.13
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install black toml
- name: Format source w/ black
# fail when file(s) would be formatted
run: black --check arrow_expts tests

lint:
if: |
(github.event_name == 'push' || github.event.pull_request.head.repo.fork) &&
(!contains(github.event.head_commit.message, 'skip ci'))

strategy:
matrix:
pyver: ["py310", "py313"]

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Ruff with Python target version ${{ matrix.pyver }}
uses: chartboost/ruff-action@v1
with:
src: arrow_expts
args: "check --target-version ${{ matrix.pyver }}"

test:
if: |
(github.event_name == 'push' || github.event.pull_request.head.repo.fork) &&
(!contains(github.event.head_commit.message, 'skip ci'))
needs: [format, lint]

strategy:
matrix:
python-version: ["3.10", "3.13"]
os: [ubuntu-latest, windows-latest, macos-latest]
# include:
# - python-version: "3.14"
# os: ubuntu-latest

runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
allow-prereleases: true
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .
- name: Run pytest
run: |
pytest -vv tests

type-hints:
if: |
(github.event_name == 'push' || github.event.pull_request.head.repo.fork) &&
(!contains(github.event.head_commit.message, 'skip ci'))

strategy:
matrix:
python-version: ["3.10", "3.13", "3.14"]

runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
allow-prereleases: true
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy{,_extensions}
- name: Type check w/ mypy
run: echo y | mypy --install-types --check-untyped-defs --pretty arrow_expts
45 changes: 45 additions & 0 deletions arrow_expts/schema/json-blob-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@
},
"type": "array"
},
{
"items": {
"anyOf": [
{
"format": "binary",
"type": "string"
},
{
"type": "null"
}
]
},
"type": "array"
},
{
"items": {
"anyOf": [
Expand Down Expand Up @@ -112,6 +126,7 @@
"integer",
"number",
"boolean",
"bytes",
"date-time",
"duration",
"time-pattern"
Expand Down Expand Up @@ -280,6 +295,20 @@
},
"type": "array"
},
{
"items": {
"anyOf": [
{
"format": "binary",
"type": "string"
},
{
"type": "null"
}
]
},
"type": "array"
},
{
"items": {
"anyOf": [
Expand Down Expand Up @@ -331,6 +360,7 @@
"integer",
"number",
"boolean",
"bytes",
"date-time",
"duration",
"time-pattern"
Expand Down Expand Up @@ -501,6 +531,20 @@
},
"type": "array"
},
{
"items": {
"anyOf": [
{
"format": "binary",
"type": "string"
},
{
"type": "null"
}
]
},
"type": "array"
},
{
"items": {
"anyOf": [
Expand Down Expand Up @@ -552,6 +596,7 @@
"integer",
"number",
"boolean",
"bytes",
"date-time",
"duration",
"time-pattern"
Expand Down
79 changes: 64 additions & 15 deletions arrow_expts/schema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,100 @@
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "python-dateutil",
# "pandas>=2",
# "pydantic>=2",
# ]
# ///

"""Write JSON schema for JSON blob in SpineDB
"""Write JSON schema for JSON blob in SpineDB"""

"""

# from dataclasses import dataclass
# from dataclasses import field
from datetime import datetime, timedelta
from typing import Annotated, Literal, Type, TypeAlias
from typing import Annotated, Literal, TypeAlias

from dateutil.relativedelta import relativedelta
import numpy as np
import pandas as pd
from pydantic import RootModel
from pydantic.dataclasses import dataclass
from pydantic.dataclasses import Field as field
from pydantic.types import StringConstraints

if __name__ == "__main__":
from pydantic.dataclasses import dataclass
from pydantic.dataclasses import Field as field
else:
from dataclasses import dataclass
from dataclasses import field


Floats: TypeAlias = list[float]
Integers: TypeAlias = list[int]
Strings: TypeAlias = list[str]
Booleans: TypeAlias = list[bool]
BytesList: TypeAlias = list[bytes] # array of bytes to support mixed types

Datetimes: TypeAlias = list[datetime]
Timedeltas: TypeAlias = list[timedelta]

# FIXME: how to do w/o Pydantic?
time_pat_re = r"(Y|M|D|WD|h|m|s)[0-9]+-[0-9]+"
TimePattern: TypeAlias = Annotated[str, StringConstraints(pattern=time_pat_re)]

# generate schema w/ Pydantic by running as a script
if __name__ == "__main__":
from pydantic.types import StringConstraints

TimePattern: TypeAlias = Annotated[str, StringConstraints(pattern=time_pat_re)]
else:
from re import Pattern

@dataclass(frozen=True)
class TimePattern:
pattern: str
re: str | Pattern[str] = time_pat_re


TimePatterns: TypeAlias = list[TimePattern]

# nullable variant of arrays
NullableIntegers: TypeAlias = list[int | None]
NullableFloats: TypeAlias = list[float | None]
NullableStrings: TypeAlias = list[str | None]
NullableBooleans: TypeAlias = list[bool | None]
NullableBytesList: TypeAlias = list[bytes | None]
NullableDatetimes: TypeAlias = list[datetime | None]
NullableTimedeltas: TypeAlias = list[timedelta | None]
NullableTimePatterns: TypeAlias = list[TimePattern | None]

# sets of types used to define array schemas below
IndexTypes: TypeAlias = Integers | Strings | Datetimes | Timedeltas | TimePatterns
ValueTypes: TypeAlias = (
Integers | Strings | Floats | Booleans | Datetimes | Timedeltas | TimePatterns
Integers
| Strings
| Floats
| Booleans
| Datetimes
| Timedeltas
| TimePatterns
| BytesList
)
NullableValueTypes: TypeAlias = (
NullableIntegers
| NullableStrings
| NullableFloats
| NullableBooleans
| NullableBytesList
| NullableDatetimes
| NullableTimedeltas
| NullableTimePatterns
)


# names of types used in the schema
ValueTypeNames: TypeAlias = Literal[
"string", "integer", "number", "boolean", "date-time", "duration", "time-pattern"
"string",
"integer",
"number",
"boolean",
"bytes",
"date-time",
"duration",
"time-pattern",
]
IndexValueTypeNames: TypeAlias = Literal[
"string", "integer", "date-time", "duration", "time-pattern"
Expand All @@ -69,13 +104,25 @@
type_map: dict[type, ValueTypeNames] = {
str: "string",
int: "integer",
np.int8: "integer",
np.int16: "integer",
np.int32: "integer",
np.int64: "integer",
float: "number",
np.float16: "number",
np.float32: "number",
np.float64: "number",
# np.float128: "number", # not available on macos
bool: "boolean",
np.bool: "boolean",
datetime: "date-time",
pd.Timestamp: "date-time",
timedelta: "duration",
pd.Timedelta: "duration",
relativedelta: "duration",
pd.DateOffset: "duration",
TimePattern: "time-pattern",
bytes: "string",
}


Expand Down Expand Up @@ -184,7 +231,7 @@ class Array(_TypeInferMixin):

# NOTE: To add run-length encoding to the schema, add it to the
# following type union following which, we need to implement a
# converter to an Arrow array type
# converter to a compatible pyarrow array type
Table: TypeAlias = list[
RunEndIndex | DictEncodedIndex | ArrayIndex | RunEndArray | DictEncodedArray | Array
]
Expand All @@ -195,6 +242,8 @@ class Array(_TypeInferMixin):
import json
from pathlib import Path

from pydantic import RootModel

parser = ArgumentParser(__doc__)
parser.add_argument("json_file", help="Path of JSON schema file to write")
opts = parser.parse_args()
Expand Down
Loading