|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | from datetime import time |
4 | | -from typing import Annotated, Any, Literal, TypeVar, Union |
| 4 | +from typing import Any, Literal, TypeVar, Union |
5 | 5 |
|
6 | 6 | import pandas as pd |
7 | | -from pydantic import ( |
8 | | - BeforeValidator, |
9 | | - Field, |
10 | | -) |
11 | 7 |
|
12 | 8 | GeoFileTypes = Literal["json", "geojson", "shp", "parquet", "csv", "txt"] |
13 | 9 |
|
14 | 10 | TransitFileTypes = Literal["txt", "csv", "parquet"] |
15 | 11 |
|
16 | | - |
17 | 12 | RoadwayFileTypes = Literal["geojson", "shp", "parquet", "json"] |
18 | 13 |
|
19 | | - |
20 | 14 | PandasDataFrame = TypeVar("PandasDataFrame", bound=pd.DataFrame) |
21 | 15 | PandasSeries = TypeVar("PandasSeries", bound=pd.Series) |
22 | 16 |
|
| 17 | +ForcedStr = Any # For simplicity, since BeforeValidator is not used here |
23 | 18 |
|
24 | | -ForcedStr = Annotated[Any, BeforeValidator(lambda x: str(x))] |
| 19 | +OneOf = list[list[Union[str, list[str]]]] |
| 20 | +ConflictsWith = list[list[str]] |
| 21 | +AnyOf = list[list[Union[str, list[str]]]] |
25 | 22 |
|
| 23 | +Latitude = float |
| 24 | +Longitude = float |
| 25 | +PhoneNum = str |
| 26 | +TimeString = str |
26 | 27 |
|
27 | | -OneOf = Annotated[ |
28 | | - list[list[Union[str, list[str]]]], |
29 | | - Field( |
30 | | - description=["List fields where at least one is required for the data model to be valid."] |
31 | | - ), |
32 | | -] |
33 | 28 |
|
34 | | -ConflictsWith = Annotated[ |
35 | | - list[list[str]], |
36 | | - Field( |
37 | | - description=[ |
38 | | - "List of pairs of fields where if one is present, the other cannot be present." |
39 | | - ] |
40 | | - ), |
41 | | -] |
| 29 | +# Standalone validator for timespan strings |
| 30 | +def validate_timespan_string(value: Any) -> list[str]: |
| 31 | + """Validate that value is a list of exactly 2 time strings in HH:MM or HH:MM:SS format. |
42 | 32 |
|
43 | | -AnyOf = Annotated[ |
44 | | - list[list[Union[str, list[str]]]], |
45 | | - Field(description=["List fields where any are required for the data model to be valid."]), |
46 | | -] |
| 33 | + Returns the value if valid, raises ValueError otherwise. |
| 34 | + """ |
| 35 | + if not isinstance(value, list): |
| 36 | + msg = "TimespanString must be a list" |
| 37 | + raise ValueError(msg) |
| 38 | + REQUIRED_LENGTH = 2 |
| 39 | + if len(value) != REQUIRED_LENGTH: |
| 40 | + msg = f"TimespanString must have exactly {REQUIRED_LENGTH} elements" |
| 41 | + raise ValueError(msg) |
| 42 | + for item in value: |
| 43 | + if not isinstance(item, str): |
| 44 | + msg = "TimespanString elements must be strings" |
| 45 | + raise ValueError(msg) |
| 46 | + import re # noqa: PLC0415 |
47 | 47 |
|
48 | | -Latitude = Annotated[float, Field(ge=-90, le=90, description="Latitude of stop.")] |
| 48 | + if not re.match(r"^(\d+):([0-5]\d)(:[0-5]\d)?$", item): |
| 49 | + msg = f"Invalid time format: {item}" |
| 50 | + raise ValueError(msg) |
| 51 | + return value |
49 | 52 |
|
50 | | -Longitude = Annotated[float, Field(ge=-180, le=180, description="Longitude of stop.")] |
51 | 53 |
|
52 | | -PhoneNum = Annotated[str, Field("", description="Phone number for the specified location.")] |
53 | | -TimeString = Annotated[ |
54 | | - str, |
55 | | - Field( |
56 | | - description="A time string in the format HH:MM or HH:MM:SS", |
57 | | - pattern=r"^(\d+):([0-5]\d)(:[0-5]\d)?$", |
58 | | - ), |
59 | | -] |
60 | | -TimespanString = Annotated[ |
61 | | - list[TimeString], |
62 | | - Field(min_length=2, max_length=2), |
63 | | -] |
| 54 | +TimespanString = list[str] |
64 | 55 | TimeType = Union[time, str, int] |
0 commit comments