sqlmesh.core.model.meta
1from __future__ import annotations 2 3import typing as t 4from enum import Enum 5 6from croniter import croniter 7from pydantic import Field, root_validator, validator 8from sqlglot import exp, maybe_parse 9 10import sqlmesh.core.dialect as d 11from sqlmesh.core.model.kind import ( 12 IncrementalByTimeRangeKind, 13 IncrementalByUniqueKeyKind, 14 ModelKind, 15 ModelKindName, 16 TimeColumn, 17 model_kind_validator, 18) 19from sqlmesh.utils import unique 20from sqlmesh.utils.date import TimeLike, preserve_time_like_kind, to_datetime 21from sqlmesh.utils.errors import ConfigError 22from sqlmesh.utils.pydantic import PydanticModel 23 24 25class IntervalUnit(str, Enum): 26 """IntervalUnit is the inferred granularity of an incremental model. 27 28 IntervalUnit can be one of 4 types, DAY, HOUR, MINUTE. The unit is inferred 29 based on the cron schedule of a model. The minimum time delta between a sample set of dates 30 is used to determine which unit a model's schedule is. 31 """ 32 33 DAY = "day" 34 HOUR = "hour" 35 MINUTE = "minute" 36 37 38HookCall = t.Union[exp.Expression, t.Tuple[str, t.Dict[str, exp.Expression]]] 39AuditReference = t.Tuple[str, t.Dict[str, exp.Expression]] 40 41 42class ModelMeta(PydanticModel): 43 """Metadata for models which can be defined in SQL.""" 44 45 name: str 46 kind: ModelKind = ModelKind(name=ModelKindName.VIEW) 47 dialect: str = "" 48 cron: str = "@daily" 49 owner: t.Optional[str] 50 description: t.Optional[str] 51 stamp: t.Optional[str] 52 start: t.Optional[TimeLike] 53 batch_size: t.Optional[int] 54 storage_format: t.Optional[str] 55 partitioned_by_: t.List[str] = Field(default=[], alias="partitioned_by") 56 pre: t.List[HookCall] = [] 57 post: t.List[HookCall] = [] 58 depends_on_: t.Optional[t.Set[str]] = Field(default=None, alias="depends_on") 59 columns_to_types_: t.Optional[t.Dict[str, exp.DataType]] = Field(default=None, alias="columns") 60 column_descriptions_: t.Optional[t.Dict[str, str]] 61 audits: t.List[AuditReference] = [] 62 63 _croniter: t.Optional[croniter] = None 64 65 _model_kind_validator = model_kind_validator 66 67 @validator("audits", pre=True) 68 def _audits_validator(cls, v: t.Any) -> t.Any: 69 def extract(v: exp.Expression) -> t.Tuple[str, t.Dict[str, str]]: 70 kwargs = {} 71 72 if isinstance(v, exp.Anonymous): 73 func = v.name 74 args = v.expressions 75 elif isinstance(v, exp.Func): 76 func = v.sql_name() 77 args = list(v.args.values()) 78 else: 79 return v.name.lower(), {} 80 81 for arg in args: 82 if not isinstance(arg, exp.EQ): 83 raise ConfigError( 84 f"Function '{func}' must be called with key-value arguments like {func}(arg=value)." 85 ) 86 kwargs[arg.left.name] = arg.right 87 return (func.lower(), kwargs) 88 89 if isinstance(v, (exp.Tuple, exp.Array)): 90 return [extract(i) for i in v.expressions] 91 if isinstance(v, exp.Paren): 92 return [extract(v.this)] 93 if isinstance(v, exp.Expression): 94 return [extract(v)] 95 if isinstance(v, list): 96 return [ 97 ( 98 entry[0].lower(), 99 { 100 key: d.parse(value)[0] if isinstance(value, str) else value 101 for key, value in entry[1].items() 102 }, 103 ) 104 for entry in v 105 ] 106 return v 107 108 @validator("pre", "post", pre=True) 109 def _value_or_tuple_with_args_validator(cls, v: t.Any) -> t.Any: 110 def extract(v: exp.Expression) -> t.Union[exp.Expression, t.Tuple[str, t.Dict[str, str]]]: 111 kwargs = {} 112 113 if isinstance(v, exp.Anonymous): 114 func = v.name 115 args = v.expressions 116 elif not isinstance(v, d.Jinja) and isinstance(v, exp.Func): 117 func = v.sql_name() 118 args = list(v.args.values()) 119 elif isinstance(v, exp.Column): 120 return v.name.lower(), {} 121 else: 122 return v 123 124 for arg in args: 125 if not isinstance(arg, exp.EQ): 126 raise ConfigError( 127 f"Function '{func}' must be called with key-value arguments like {func}(arg=value)." 128 ) 129 kwargs[arg.left.name] = arg.right 130 return (func.lower(), kwargs) 131 132 if isinstance(v, (exp.Tuple, exp.Array)): 133 items: t.List[t.Any] = [] 134 for item in v.expressions: 135 if isinstance(item, exp.Literal): 136 items.append(d.parse(item.this)[0]) 137 elif isinstance(item, exp.Column) and isinstance(item.this, exp.Identifier): 138 items.append(d.parse(item.this.this)[0]) 139 else: 140 items.append(extract(item)) 141 return items 142 if isinstance(v, exp.Paren): 143 return [extract(v.this)] 144 if isinstance(v, exp.Expression): 145 return [extract(v)] 146 if isinstance(v, list) and v: 147 transformed = [] 148 for entry in v: 149 if isinstance(entry, exp.Expression): 150 transformed.append(extract(entry)) 151 elif isinstance(entry, str): 152 transformed.append(d.parse(entry)[0]) 153 else: 154 transformed.append( 155 ( 156 entry[0].lower(), 157 { 158 key: d.parse(value)[0] if isinstance(value, str) else value 159 for key, value in entry[1].items() 160 }, 161 ) 162 ) 163 return transformed 164 165 return v 166 167 @validator("partitioned_by_", pre=True) 168 def _value_or_tuple_validator(cls, v: t.Any) -> t.Any: 169 if isinstance(v, (exp.Tuple, exp.Array)): 170 return [e.name for e in v.expressions] 171 if isinstance(v, exp.Expression): 172 return [v.name] 173 return v 174 175 @validator("dialect", "owner", "storage_format", "description", "stamp", pre=True) 176 def _string_validator(cls, v: t.Any) -> t.Optional[str]: 177 if isinstance(v, exp.Expression): 178 return v.name 179 return str(v) if v is not None else None 180 181 @validator("cron", pre=True) 182 def _cron_validator(cls, v: t.Any) -> t.Optional[str]: 183 cron = cls._string_validator(v) 184 if cron: 185 try: 186 croniter(cron) 187 except Exception: 188 raise ConfigError(f"Invalid cron expression '{cron}'") 189 return cron 190 191 @validator("columns_to_types_", pre=True) 192 def _columns_validator(cls, v: t.Any) -> t.Optional[t.Dict[str, exp.DataType]]: 193 if isinstance(v, exp.Schema): 194 return {column.name: column.args["kind"] for column in v.expressions} 195 if isinstance(v, dict): 196 return { 197 k: maybe_parse(data_type, into=exp.DataType) # type: ignore 198 for k, data_type in v.items() 199 } 200 return v 201 202 @validator("depends_on_", pre=True) 203 def _depends_on_validator(cls, v: t.Any) -> t.Optional[t.Set[str]]: 204 if isinstance(v, (exp.Array, exp.Tuple)): 205 return { 206 exp.table_name(table.name if table.is_string else table.sql()) 207 for table in v.expressions 208 } 209 if isinstance(v, exp.Expression): 210 return {exp.table_name(v.sql())} 211 return v 212 213 @validator("start", pre=True) 214 def _date_validator(cls, v: t.Any) -> t.Optional[TimeLike]: 215 if isinstance(v, exp.Expression): 216 v = v.name 217 if v and not to_datetime(v): 218 raise ConfigError(f"'{v}' not a valid date time") 219 return v 220 221 @validator("batch_size", pre=True) 222 def _int_validator(cls, v: t.Any) -> t.Optional[int]: 223 if not isinstance(v, exp.Expression): 224 batch_size = int(v) if v is not None else None 225 else: 226 batch_size = int(v.name) 227 if batch_size is not None and batch_size <= 0: 228 raise ConfigError( 229 f"Invalid batch size {batch_size}. The value should be greater than 0" 230 ) 231 return batch_size 232 233 @root_validator 234 def _kind_validator(cls, values: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: 235 kind = values.get("kind") 236 if kind and not kind.is_materialized: 237 if values.get("partitioned_by_"): 238 raise ValueError(f"partitioned_by field cannot be set for {kind} models") 239 return values 240 241 @property 242 def time_column(self) -> t.Optional[TimeColumn]: 243 if isinstance(self.kind, IncrementalByTimeRangeKind): 244 return self.kind.time_column 245 return None 246 247 @property 248 def unique_key(self) -> t.List[str]: 249 if isinstance(self.kind, IncrementalByUniqueKeyKind): 250 return self.kind.unique_key 251 return [] 252 253 @property 254 def partitioned_by(self) -> t.List[str]: 255 time_column = [self.time_column.column] if self.time_column else [] 256 return unique([*time_column, *self.partitioned_by_]) 257 258 @property 259 def column_descriptions(self) -> t.Dict[str, str]: 260 """A dictionary of column names to annotation comments.""" 261 return self.column_descriptions_ or {} 262 263 def interval_unit(self, sample_size: int = 10) -> IntervalUnit: 264 """Returns the IntervalUnit of the model 265 266 The interval unit is used to determine the lag applied to start_date and end_date for model rendering and intervals. 267 268 Args: 269 sample_size: The number of samples to take from the cron to infer the unit. 270 271 Returns: 272 The IntervalUnit enum. 273 """ 274 schedule = croniter(self.cron) 275 samples = [schedule.get_next() for _ in range(sample_size)] 276 min_interval = min(b - a for a, b in zip(samples, samples[1:])) 277 if min_interval >= 86400: 278 return IntervalUnit.DAY 279 elif min_interval >= 3600: 280 return IntervalUnit.HOUR 281 return IntervalUnit.MINUTE 282 283 def normalized_cron(self) -> str: 284 """Returns the UTC normalized cron based on sampling heuristics. 285 286 SQLMesh supports 3 interval units, daily, hourly, and minutes. If a job is scheduled 287 daily at 1PM, the actual intervals are shifted back to midnight UTC. 288 289 Returns: 290 The cron string representing either daily, hourly, or minutes. 291 """ 292 unit = self.interval_unit() 293 if unit == IntervalUnit.MINUTE: 294 return "* * * * *" 295 if unit == IntervalUnit.HOUR: 296 return "0 * * * *" 297 if unit == IntervalUnit.DAY: 298 return "0 0 * * *" 299 return "" 300 301 def croniter(self, value: TimeLike) -> croniter: 302 if self._croniter is None: 303 self._croniter = croniter(self.normalized_cron()) 304 self._croniter.set_current(to_datetime(value)) 305 return self._croniter 306 307 def cron_next(self, value: TimeLike) -> TimeLike: 308 """ 309 Get the next timestamp given a time-like value and the model's cron. 310 311 Args: 312 value: A variety of date formats. 313 314 Returns: 315 The timestamp for the next run. 316 """ 317 return preserve_time_like_kind(value, self.croniter(value).get_next()) 318 319 def cron_prev(self, value: TimeLike) -> TimeLike: 320 """ 321 Get the previous timestamp given a time-like value and the model's cron. 322 323 Args: 324 value: A variety of date formats. 325 326 Returns: 327 The timestamp for the previous run. 328 """ 329 return preserve_time_like_kind(value, self.croniter(value).get_prev()) 330 331 def cron_floor(self, value: TimeLike) -> TimeLike: 332 """ 333 Get the floor timestamp given a time-like value and the model's cron. 334 335 Args: 336 value: A variety of date formats. 337 338 Returns: 339 The timestamp floor. 340 """ 341 return preserve_time_like_kind(value, self.croniter(self.cron_next(value)).get_prev())
26class IntervalUnit(str, Enum): 27 """IntervalUnit is the inferred granularity of an incremental model. 28 29 IntervalUnit can be one of 4 types, DAY, HOUR, MINUTE. The unit is inferred 30 based on the cron schedule of a model. The minimum time delta between a sample set of dates 31 is used to determine which unit a model's schedule is. 32 """ 33 34 DAY = "day" 35 HOUR = "hour" 36 MINUTE = "minute"
IntervalUnit is the inferred granularity of an incremental model.
IntervalUnit can be one of 4 types, DAY, HOUR, MINUTE. The unit is inferred based on the cron schedule of a model. The minimum time delta between a sample set of dates is used to determine which unit a model's schedule is.
Inherited Members
- enum.Enum
- name
- value
- builtins.str
- encode
- replace
- split
- rsplit
- join
- capitalize
- casefold
- title
- center
- count
- expandtabs
- find
- partition
- index
- ljust
- lower
- lstrip
- rfind
- rindex
- rjust
- rstrip
- rpartition
- splitlines
- strip
- swapcase
- translate
- upper
- startswith
- endswith
- removeprefix
- removesuffix
- isascii
- islower
- isupper
- istitle
- isspace
- isdecimal
- isdigit
- isnumeric
- isalpha
- isalnum
- isidentifier
- isprintable
- zfill
- format
- format_map
- maketrans
43class ModelMeta(PydanticModel): 44 """Metadata for models which can be defined in SQL.""" 45 46 name: str 47 kind: ModelKind = ModelKind(name=ModelKindName.VIEW) 48 dialect: str = "" 49 cron: str = "@daily" 50 owner: t.Optional[str] 51 description: t.Optional[str] 52 stamp: t.Optional[str] 53 start: t.Optional[TimeLike] 54 batch_size: t.Optional[int] 55 storage_format: t.Optional[str] 56 partitioned_by_: t.List[str] = Field(default=[], alias="partitioned_by") 57 pre: t.List[HookCall] = [] 58 post: t.List[HookCall] = [] 59 depends_on_: t.Optional[t.Set[str]] = Field(default=None, alias="depends_on") 60 columns_to_types_: t.Optional[t.Dict[str, exp.DataType]] = Field(default=None, alias="columns") 61 column_descriptions_: t.Optional[t.Dict[str, str]] 62 audits: t.List[AuditReference] = [] 63 64 _croniter: t.Optional[croniter] = None 65 66 _model_kind_validator = model_kind_validator 67 68 @validator("audits", pre=True) 69 def _audits_validator(cls, v: t.Any) -> t.Any: 70 def extract(v: exp.Expression) -> t.Tuple[str, t.Dict[str, str]]: 71 kwargs = {} 72 73 if isinstance(v, exp.Anonymous): 74 func = v.name 75 args = v.expressions 76 elif isinstance(v, exp.Func): 77 func = v.sql_name() 78 args = list(v.args.values()) 79 else: 80 return v.name.lower(), {} 81 82 for arg in args: 83 if not isinstance(arg, exp.EQ): 84 raise ConfigError( 85 f"Function '{func}' must be called with key-value arguments like {func}(arg=value)." 86 ) 87 kwargs[arg.left.name] = arg.right 88 return (func.lower(), kwargs) 89 90 if isinstance(v, (exp.Tuple, exp.Array)): 91 return [extract(i) for i in v.expressions] 92 if isinstance(v, exp.Paren): 93 return [extract(v.this)] 94 if isinstance(v, exp.Expression): 95 return [extract(v)] 96 if isinstance(v, list): 97 return [ 98 ( 99 entry[0].lower(), 100 { 101 key: d.parse(value)[0] if isinstance(value, str) else value 102 for key, value in entry[1].items() 103 }, 104 ) 105 for entry in v 106 ] 107 return v 108 109 @validator("pre", "post", pre=True) 110 def _value_or_tuple_with_args_validator(cls, v: t.Any) -> t.Any: 111 def extract(v: exp.Expression) -> t.Union[exp.Expression, t.Tuple[str, t.Dict[str, str]]]: 112 kwargs = {} 113 114 if isinstance(v, exp.Anonymous): 115 func = v.name 116 args = v.expressions 117 elif not isinstance(v, d.Jinja) and isinstance(v, exp.Func): 118 func = v.sql_name() 119 args = list(v.args.values()) 120 elif isinstance(v, exp.Column): 121 return v.name.lower(), {} 122 else: 123 return v 124 125 for arg in args: 126 if not isinstance(arg, exp.EQ): 127 raise ConfigError( 128 f"Function '{func}' must be called with key-value arguments like {func}(arg=value)." 129 ) 130 kwargs[arg.left.name] = arg.right 131 return (func.lower(), kwargs) 132 133 if isinstance(v, (exp.Tuple, exp.Array)): 134 items: t.List[t.Any] = [] 135 for item in v.expressions: 136 if isinstance(item, exp.Literal): 137 items.append(d.parse(item.this)[0]) 138 elif isinstance(item, exp.Column) and isinstance(item.this, exp.Identifier): 139 items.append(d.parse(item.this.this)[0]) 140 else: 141 items.append(extract(item)) 142 return items 143 if isinstance(v, exp.Paren): 144 return [extract(v.this)] 145 if isinstance(v, exp.Expression): 146 return [extract(v)] 147 if isinstance(v, list) and v: 148 transformed = [] 149 for entry in v: 150 if isinstance(entry, exp.Expression): 151 transformed.append(extract(entry)) 152 elif isinstance(entry, str): 153 transformed.append(d.parse(entry)[0]) 154 else: 155 transformed.append( 156 ( 157 entry[0].lower(), 158 { 159 key: d.parse(value)[0] if isinstance(value, str) else value 160 for key, value in entry[1].items() 161 }, 162 ) 163 ) 164 return transformed 165 166 return v 167 168 @validator("partitioned_by_", pre=True) 169 def _value_or_tuple_validator(cls, v: t.Any) -> t.Any: 170 if isinstance(v, (exp.Tuple, exp.Array)): 171 return [e.name for e in v.expressions] 172 if isinstance(v, exp.Expression): 173 return [v.name] 174 return v 175 176 @validator("dialect", "owner", "storage_format", "description", "stamp", pre=True) 177 def _string_validator(cls, v: t.Any) -> t.Optional[str]: 178 if isinstance(v, exp.Expression): 179 return v.name 180 return str(v) if v is not None else None 181 182 @validator("cron", pre=True) 183 def _cron_validator(cls, v: t.Any) -> t.Optional[str]: 184 cron = cls._string_validator(v) 185 if cron: 186 try: 187 croniter(cron) 188 except Exception: 189 raise ConfigError(f"Invalid cron expression '{cron}'") 190 return cron 191 192 @validator("columns_to_types_", pre=True) 193 def _columns_validator(cls, v: t.Any) -> t.Optional[t.Dict[str, exp.DataType]]: 194 if isinstance(v, exp.Schema): 195 return {column.name: column.args["kind"] for column in v.expressions} 196 if isinstance(v, dict): 197 return { 198 k: maybe_parse(data_type, into=exp.DataType) # type: ignore 199 for k, data_type in v.items() 200 } 201 return v 202 203 @validator("depends_on_", pre=True) 204 def _depends_on_validator(cls, v: t.Any) -> t.Optional[t.Set[str]]: 205 if isinstance(v, (exp.Array, exp.Tuple)): 206 return { 207 exp.table_name(table.name if table.is_string else table.sql()) 208 for table in v.expressions 209 } 210 if isinstance(v, exp.Expression): 211 return {exp.table_name(v.sql())} 212 return v 213 214 @validator("start", pre=True) 215 def _date_validator(cls, v: t.Any) -> t.Optional[TimeLike]: 216 if isinstance(v, exp.Expression): 217 v = v.name 218 if v and not to_datetime(v): 219 raise ConfigError(f"'{v}' not a valid date time") 220 return v 221 222 @validator("batch_size", pre=True) 223 def _int_validator(cls, v: t.Any) -> t.Optional[int]: 224 if not isinstance(v, exp.Expression): 225 batch_size = int(v) if v is not None else None 226 else: 227 batch_size = int(v.name) 228 if batch_size is not None and batch_size <= 0: 229 raise ConfigError( 230 f"Invalid batch size {batch_size}. The value should be greater than 0" 231 ) 232 return batch_size 233 234 @root_validator 235 def _kind_validator(cls, values: t.Dict[str, t.Any]) -> t.Dict[str, t.Any]: 236 kind = values.get("kind") 237 if kind and not kind.is_materialized: 238 if values.get("partitioned_by_"): 239 raise ValueError(f"partitioned_by field cannot be set for {kind} models") 240 return values 241 242 @property 243 def time_column(self) -> t.Optional[TimeColumn]: 244 if isinstance(self.kind, IncrementalByTimeRangeKind): 245 return self.kind.time_column 246 return None 247 248 @property 249 def unique_key(self) -> t.List[str]: 250 if isinstance(self.kind, IncrementalByUniqueKeyKind): 251 return self.kind.unique_key 252 return [] 253 254 @property 255 def partitioned_by(self) -> t.List[str]: 256 time_column = [self.time_column.column] if self.time_column else [] 257 return unique([*time_column, *self.partitioned_by_]) 258 259 @property 260 def column_descriptions(self) -> t.Dict[str, str]: 261 """A dictionary of column names to annotation comments.""" 262 return self.column_descriptions_ or {} 263 264 def interval_unit(self, sample_size: int = 10) -> IntervalUnit: 265 """Returns the IntervalUnit of the model 266 267 The interval unit is used to determine the lag applied to start_date and end_date for model rendering and intervals. 268 269 Args: 270 sample_size: The number of samples to take from the cron to infer the unit. 271 272 Returns: 273 The IntervalUnit enum. 274 """ 275 schedule = croniter(self.cron) 276 samples = [schedule.get_next() for _ in range(sample_size)] 277 min_interval = min(b - a for a, b in zip(samples, samples[1:])) 278 if min_interval >= 86400: 279 return IntervalUnit.DAY 280 elif min_interval >= 3600: 281 return IntervalUnit.HOUR 282 return IntervalUnit.MINUTE 283 284 def normalized_cron(self) -> str: 285 """Returns the UTC normalized cron based on sampling heuristics. 286 287 SQLMesh supports 3 interval units, daily, hourly, and minutes. If a job is scheduled 288 daily at 1PM, the actual intervals are shifted back to midnight UTC. 289 290 Returns: 291 The cron string representing either daily, hourly, or minutes. 292 """ 293 unit = self.interval_unit() 294 if unit == IntervalUnit.MINUTE: 295 return "* * * * *" 296 if unit == IntervalUnit.HOUR: 297 return "0 * * * *" 298 if unit == IntervalUnit.DAY: 299 return "0 0 * * *" 300 return "" 301 302 def croniter(self, value: TimeLike) -> croniter: 303 if self._croniter is None: 304 self._croniter = croniter(self.normalized_cron()) 305 self._croniter.set_current(to_datetime(value)) 306 return self._croniter 307 308 def cron_next(self, value: TimeLike) -> TimeLike: 309 """ 310 Get the next timestamp given a time-like value and the model's cron. 311 312 Args: 313 value: A variety of date formats. 314 315 Returns: 316 The timestamp for the next run. 317 """ 318 return preserve_time_like_kind(value, self.croniter(value).get_next()) 319 320 def cron_prev(self, value: TimeLike) -> TimeLike: 321 """ 322 Get the previous timestamp given a time-like value and the model's cron. 323 324 Args: 325 value: A variety of date formats. 326 327 Returns: 328 The timestamp for the previous run. 329 """ 330 return preserve_time_like_kind(value, self.croniter(value).get_prev()) 331 332 def cron_floor(self, value: TimeLike) -> TimeLike: 333 """ 334 Get the floor timestamp given a time-like value and the model's cron. 335 336 Args: 337 value: A variety of date formats. 338 339 Returns: 340 The timestamp floor. 341 """ 342 return preserve_time_like_kind(value, self.croniter(self.cron_next(value)).get_prev())
Metadata for models which can be defined in SQL.
264 def interval_unit(self, sample_size: int = 10) -> IntervalUnit: 265 """Returns the IntervalUnit of the model 266 267 The interval unit is used to determine the lag applied to start_date and end_date for model rendering and intervals. 268 269 Args: 270 sample_size: The number of samples to take from the cron to infer the unit. 271 272 Returns: 273 The IntervalUnit enum. 274 """ 275 schedule = croniter(self.cron) 276 samples = [schedule.get_next() for _ in range(sample_size)] 277 min_interval = min(b - a for a, b in zip(samples, samples[1:])) 278 if min_interval >= 86400: 279 return IntervalUnit.DAY 280 elif min_interval >= 3600: 281 return IntervalUnit.HOUR 282 return IntervalUnit.MINUTE
Returns the IntervalUnit of the model
The interval unit is used to determine the lag applied to start_date and end_date for model rendering and intervals.
Arguments:
- sample_size: The number of samples to take from the cron to infer the unit.
Returns:
The IntervalUnit enum.
284 def normalized_cron(self) -> str: 285 """Returns the UTC normalized cron based on sampling heuristics. 286 287 SQLMesh supports 3 interval units, daily, hourly, and minutes. If a job is scheduled 288 daily at 1PM, the actual intervals are shifted back to midnight UTC. 289 290 Returns: 291 The cron string representing either daily, hourly, or minutes. 292 """ 293 unit = self.interval_unit() 294 if unit == IntervalUnit.MINUTE: 295 return "* * * * *" 296 if unit == IntervalUnit.HOUR: 297 return "0 * * * *" 298 if unit == IntervalUnit.DAY: 299 return "0 0 * * *" 300 return ""
Returns the UTC normalized cron based on sampling heuristics.
SQLMesh supports 3 interval units, daily, hourly, and minutes. If a job is scheduled daily at 1PM, the actual intervals are shifted back to midnight UTC.
Returns:
The cron string representing either daily, hourly, or minutes.
308 def cron_next(self, value: TimeLike) -> TimeLike: 309 """ 310 Get the next timestamp given a time-like value and the model's cron. 311 312 Args: 313 value: A variety of date formats. 314 315 Returns: 316 The timestamp for the next run. 317 """ 318 return preserve_time_like_kind(value, self.croniter(value).get_next())
Get the next timestamp given a time-like value and the model's cron.
Arguments:
- value: A variety of date formats.
Returns:
The timestamp for the next run.
320 def cron_prev(self, value: TimeLike) -> TimeLike: 321 """ 322 Get the previous timestamp given a time-like value and the model's cron. 323 324 Args: 325 value: A variety of date formats. 326 327 Returns: 328 The timestamp for the previous run. 329 """ 330 return preserve_time_like_kind(value, self.croniter(value).get_prev())
Get the previous timestamp given a time-like value and the model's cron.
Arguments:
- value: A variety of date formats.
Returns:
The timestamp for the previous run.
332 def cron_floor(self, value: TimeLike) -> TimeLike: 333 """ 334 Get the floor timestamp given a time-like value and the model's cron. 335 336 Args: 337 value: A variety of date formats. 338 339 Returns: 340 The timestamp floor. 341 """ 342 return preserve_time_like_kind(value, self.croniter(self.cron_next(value)).get_prev())
Get the floor timestamp given a time-like value and the model's cron.
Arguments:
- value: A variety of date formats.
Returns:
The timestamp floor.
Inherited Members
- pydantic.main.BaseModel
- BaseModel
- parse_obj
- parse_raw
- parse_file
- from_orm
- construct
- copy
- schema
- schema_json
- validate
- update_forward_refs