pytrilogy 0.0.3.6__py3-none-any.whl → 0.0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pytrilogy might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pytrilogy
3
- Version: 0.0.3.6
3
+ Version: 0.0.3.7
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,8 +1,8 @@
1
- trilogy/__init__.py,sha256=R9yJSDmZQvpxjWMcp4mGFnbg7xuxUCiIVrvP8eacKj4,302
1
+ trilogy/__init__.py,sha256=nCVrjnf_bl_zZ7GmePdSoaRQ40QxFDkSFlafhQp8Cn8,302
2
2
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  trilogy/constants.py,sha256=qZ1d0hoKPPV2HHCoFwPYTVB7b6bXjpWvXd3lE-zEhy8,1494
4
- trilogy/engine.py,sha256=yOPnR7XCjWG82Gym_LLZBkYKKJdLCvqdCyt8zguNcnM,1103
5
- trilogy/executor.py,sha256=sssEPDnIDPiQtMSrt5pFiJXUfcDc6gSi4m2Eliod_BM,16844
4
+ trilogy/engine.py,sha256=3etkm2RSVKO0IkgPKkrcs33X5gN_fIMyqMNfChcsR1E,1318
5
+ trilogy/executor.py,sha256=YgSCeeYVecI9526LGSLVe2apOo7Ddsttvs_nDC9yElQ,17194
6
6
  trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
7
7
  trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
@@ -14,7 +14,7 @@ trilogy/core/env_processor.py,sha256=pFsxnluKIusGKx1z7tTnfsd_xZcPy9pZDungkjkyvI0
14
14
  trilogy/core/environment_helpers.py,sha256=oOpewPwMp8xOtx2ayeeyuLNUwr-cli7UanHKot5ebNY,7627
15
15
  trilogy/core/ergonomics.py,sha256=ASLDd0RqKWrZiG3XcKHo8nyTjaB_8xfE9t4NZ1UvGpc,1639
16
16
  trilogy/core/exceptions.py,sha256=JPYyBcit3T_pRtlHdtKSeVJkIyWUTozW2aaut25A2xI,673
17
- trilogy/core/functions.py,sha256=7Pq9jYSJd45L2pxT7AI-_rXVZmeLnmTPp8d1lA4z4Vk,24440
17
+ trilogy/core/functions.py,sha256=rIkZGzw9hpIkXvuqQ1qPWFJO1W_NPYc6T9t0wTZ55M0,24784
18
18
  trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
19
19
  trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
20
20
  trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
@@ -24,7 +24,7 @@ trilogy/core/models/author.py,sha256=oRCKWhz-i1fO1LlHWiHE3l1awCHdQ3yx6FKH9n9RxRU
24
24
  trilogy/core/models/build.py,sha256=kiq31T8LtUtgmT37m617Q2MlMvQTuAxJzwb6947EiWU,56127
25
25
  trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
26
26
  trilogy/core/models/core.py,sha256=yie1uuq62uOQ5fjob9NMJbdvQPrCErXUT7JTCuYRyjI,9697
27
- trilogy/core/models/datasource.py,sha256=c0tGxyH2WwTmAD047tr69U0a6GNVf-ug26H68yii7DA,9257
27
+ trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
28
28
  trilogy/core/models/environment.py,sha256=h06y1Dv7naw2GuFFAAyoFZmicG7a7Lu-dRoYPVfrOGo,25967
29
29
  trilogy/core/models/execute.py,sha256=ABylFQgtavjjCfFkEsFdUwfMB4UBQLHjdzQ9E67QlAE,33521
30
30
  trilogy/core/optimizations/__init__.py,sha256=EBanqTXEzf1ZEYjAneIWoIcxtMDite5-n2dQ5xcfUtg,356
@@ -71,9 +71,10 @@ trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  trilogy/dialect/base.py,sha256=u00kIIl98as1QzcduiiyyoBzxRGVeBxfeO5hWlRCAJU,40222
72
72
  trilogy/dialect/bigquery.py,sha256=mKC3zoEU232h9RtIXJjqiZ72lWH8a6S28p6wAZKrAfg,2952
73
73
  trilogy/dialect/common.py,sha256=cbTo_vamdp8pj9spSjGSH-bSZpy4FpNJ12k5vMvyT2Y,3942
74
- trilogy/dialect/config.py,sha256=UiBY2tBbNk9owx-zxP_3lN9lErEUXhXIU_bcXA18AvU,2992
74
+ trilogy/dialect/config.py,sha256=e-ZDVh7Z648JYz85JwSobTyo2cTi4lYGFMglZzB7atM,3184
75
+ trilogy/dialect/dataframe.py,sha256=ei5y91XyZHI3ydUbdQ2sInnw2qHGtgb21DNX6qff0xw,1419
75
76
  trilogy/dialect/duckdb.py,sha256=2tH_OetgLJoKf_f4bdeeB0ozGC8f0h_xQ271I8qD-Oo,3690
76
- trilogy/dialect/enums.py,sha256=JICGp0KQhxNuVDSU36mve2XTZLbPpFRWUDkjvwni4eM,3979
77
+ trilogy/dialect/enums.py,sha256=1KDgds_DC31hGxZzNI_TIggxXF7m9rIjn9KLgNf5WQU,4425
77
78
  trilogy/dialect/postgres.py,sha256=VH4EB4myjIeZTHeFU6vK00GxY9c53rCBjg2mLbdaCEE,3254
78
79
  trilogy/dialect/presto.py,sha256=bAxaDcLL21fivPg7hmBd3HJmd0yYJdPdwNgNA5ga7DE,3391
79
80
  trilogy/dialect/snowflake.py,sha256=wmao9p26jX5yIX5SC8sRAZTXkPGTvq6ixO693QTfhz8,2989
@@ -93,9 +94,9 @@ trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,178
93
94
  trilogy/parsing/trilogy.lark,sha256=EazfEvYPuvkPkNjUnVzFi0uD9baavugbSI8CyfawShk,12573
94
95
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
95
96
  trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
96
- pytrilogy-0.0.3.6.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
97
- pytrilogy-0.0.3.6.dist-info/METADATA,sha256=PBxZLl7AH82ztlgGbJbdLWwj8r3Wo2_JRsXxgh5y1Gc,8983
98
- pytrilogy-0.0.3.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
99
- pytrilogy-0.0.3.6.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
100
- pytrilogy-0.0.3.6.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
101
- pytrilogy-0.0.3.6.dist-info/RECORD,,
97
+ pytrilogy-0.0.3.7.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
98
+ pytrilogy-0.0.3.7.dist-info/METADATA,sha256=wvr0oUtX0As37OC9ljg5XnV7rblzMNvUppA4il2PtPI,8983
99
+ pytrilogy-0.0.3.7.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
100
+ pytrilogy-0.0.3.7.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
101
+ pytrilogy-0.0.3.7.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
102
+ pytrilogy-0.0.3.7.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.6"
7
+ __version__ = "0.0.3.7"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
trilogy/core/functions.py CHANGED
@@ -503,32 +503,52 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
503
503
  arg_count=1,
504
504
  ),
505
505
  FunctionType.ADD: FunctionConfig(
506
- valid_inputs={DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
506
+ valid_inputs={
507
+ DataType.INTEGER,
508
+ DataType.FLOAT,
509
+ DataType.NUMBER,
510
+ DataType.NUMERIC,
511
+ },
507
512
  output_purpose=Purpose.PROPERTY,
508
513
  output_type=DataType.INTEGER,
509
514
  arg_count=InfiniteFunctionArgs,
510
515
  ),
511
516
  FunctionType.SUBTRACT: FunctionConfig(
512
- valid_inputs={DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
517
+ valid_inputs={
518
+ DataType.INTEGER,
519
+ DataType.FLOAT,
520
+ DataType.NUMBER,
521
+ DataType.NUMERIC,
522
+ },
513
523
  output_purpose=Purpose.PROPERTY,
514
524
  output_type=DataType.INTEGER,
515
525
  arg_count=InfiniteFunctionArgs,
516
526
  ),
517
527
  FunctionType.MULTIPLY: FunctionConfig(
518
- valid_inputs={DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
528
+ valid_inputs={
529
+ DataType.INTEGER,
530
+ DataType.FLOAT,
531
+ DataType.NUMBER,
532
+ DataType.NUMERIC,
533
+ },
519
534
  output_purpose=Purpose.PROPERTY,
520
535
  output_type=DataType.INTEGER,
521
536
  arg_count=InfiniteFunctionArgs,
522
537
  ),
523
538
  FunctionType.DIVIDE: FunctionConfig(
524
- valid_inputs={DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
539
+ valid_inputs={
540
+ DataType.INTEGER,
541
+ DataType.FLOAT,
542
+ DataType.NUMBER,
543
+ DataType.NUMERIC,
544
+ },
525
545
  output_purpose=Purpose.PROPERTY,
526
546
  output_type=DataType.INTEGER,
527
547
  arg_count=InfiniteFunctionArgs,
528
548
  ),
529
549
  FunctionType.MOD: FunctionConfig(
530
550
  valid_inputs=[
531
- {DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
551
+ {DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
532
552
  {DataType.INTEGER},
533
553
  ],
534
554
  output_purpose=Purpose.PROPERTY,
@@ -537,7 +557,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
537
557
  ),
538
558
  FunctionType.ROUND: FunctionConfig(
539
559
  valid_inputs=[
540
- {DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
560
+ {DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
541
561
  {DataType.INTEGER},
542
562
  ],
543
563
  output_purpose=Purpose.PROPERTY,
@@ -117,6 +117,12 @@ class Datasource(HasUUID, Namespaced, BaseModel):
117
117
  where: Optional[WhereClause] = None
118
118
  non_partial_for: Optional[WhereClause] = None
119
119
 
120
+ @property
121
+ def safe_address(self) -> str:
122
+ if isinstance(self.address, Address):
123
+ return self.address.location
124
+ return self.address
125
+
120
126
  def __eq__(self, other):
121
127
  if not isinstance(other, Datasource):
122
128
  return False
trilogy/dialect/config.py CHANGED
@@ -1,3 +1,6 @@
1
+ from pandas import DataFrame
2
+
3
+
1
4
  class DialectConfig:
2
5
  def __init__(self):
3
6
  pass
@@ -104,3 +107,9 @@ class TrinoConfig(PrestoConfig):
104
107
  if self.schema:
105
108
  return f"trino://{self.username}:{self.password}@{self.host}:{self.port}/{self.catalog}/{self.schema}"
106
109
  return f"trino://{self.username}:{self.password}@{self.host}:{self.port}/{self.catalog}"
110
+
111
+
112
+ class DataFrameConfig(DuckDBConfig):
113
+ def __init__(self, dataframes: dict[str, DataFrame]):
114
+ super().__init__()
115
+ self.dataframes = dataframes
@@ -0,0 +1,42 @@
1
+ from typing import Any
2
+
3
+ from pandas import DataFrame
4
+ from sqlalchemy import text
5
+
6
+ from trilogy.core.models.environment import Environment
7
+ from trilogy.dialect.duckdb import DuckDBDialect
8
+ from trilogy.engine import ExecutionEngine
9
+
10
+
11
+ class DataframeDialect(DuckDBDialect):
12
+ pass
13
+
14
+
15
+ class DataframeConnectionWrapper(ExecutionEngine):
16
+ def __init__(self, engine: ExecutionEngine, dataframes: dict[str, DataFrame]):
17
+ self.engine = engine
18
+ self.dataframes = dataframes
19
+ self.connection = None
20
+
21
+ def setup(self, env: Environment, connection):
22
+ self._register_dataframes(env, connection)
23
+
24
+ def _register_dataframes(self, env: Environment, connection):
25
+ for ds in env.datasources.values():
26
+ if ds.safe_address in self.dataframes:
27
+ connection.execute(
28
+ text("register(:name, :df)"),
29
+ {"name": ds.safe_address, "df": self.dataframes[ds.safe_address]},
30
+ )
31
+ else:
32
+ raise ValueError(
33
+ f"Dataframe {ds.safe_address} not found in dataframes on connection config, have {self.dataframes.keys()}"
34
+ )
35
+ pass
36
+
37
+ def add_dataframe(self, name: str, df: DataFrame, connection, env: Environment):
38
+ self.dataframes[name] = df
39
+ self._register_dataframes(env, connection)
40
+
41
+ def connect(self) -> Any:
42
+ return self.engine.connect()
trilogy/dialect/enums.py CHANGED
@@ -16,7 +16,7 @@ def default_factory(conf: DialectConfig, config_type):
16
16
 
17
17
  if not isinstance(conf, config_type):
18
18
  raise TypeError(
19
- f"Invalid dialect configuration for type {type(config_type).__name__}"
19
+ f"Invalid dialect configuration for type {type(config_type).__name__}, is {type(conf)}"
20
20
  )
21
21
  if conf.connect_args:
22
22
  return create_engine(
@@ -33,6 +33,7 @@ class Dialects(Enum):
33
33
  TRINO = "trino"
34
34
  POSTGRES = "postgres"
35
35
  SNOWFLAKE = "snowflake"
36
+ DATAFRAME = "dataframe"
36
37
 
37
38
  @classmethod
38
39
  def _missing_(cls, value):
@@ -88,6 +89,16 @@ class Dialects(Enum):
88
89
  from trilogy.dialect.config import TrinoConfig
89
90
 
90
91
  return _engine_factory(conf, TrinoConfig)
92
+ elif self == Dialects.DATAFRAME:
93
+ from trilogy.dialect.config import DataFrameConfig
94
+ from trilogy.dialect.dataframe import DataframeConnectionWrapper
95
+
96
+ if not conf:
97
+ conf = DataFrameConfig(dataframes={})
98
+
99
+ base = _engine_factory(conf, DataFrameConfig)
100
+
101
+ return DataframeConnectionWrapper(base, dataframes=conf.dataframes)
91
102
  else:
92
103
  raise ValueError(
93
104
  f"Unsupported dialect {self} for default engine creation; create one explicitly."
trilogy/engine.py CHANGED
@@ -1,7 +1,9 @@
1
- from typing import Protocol
1
+ from typing import Any, Protocol
2
2
 
3
3
  from sqlalchemy.engine import Connection, CursorResult, Engine
4
4
 
5
+ from trilogy.core.models.environment import Environment
6
+
5
7
 
6
8
  class EngineResult(Protocol):
7
9
  pass
@@ -13,7 +15,7 @@ class EngineResult(Protocol):
13
15
  class EngineConnection(Protocol):
14
16
  pass
15
17
 
16
- def execute(self, statement: str) -> EngineResult:
18
+ def execute(self, statement: str, parameters: Any | None = None) -> EngineResult:
17
19
  pass
18
20
 
19
21
 
@@ -23,6 +25,9 @@ class ExecutionEngine(Protocol):
23
25
  def connect(self) -> EngineConnection:
24
26
  pass
25
27
 
28
+ def setup(self, env: Environment, connection):
29
+ pass
30
+
26
31
 
27
32
  ### Begin default SQLAlchemy implementation
28
33
  class SqlAlchemyResult(EngineResult):
@@ -37,8 +42,10 @@ class SqlAlchemyConnection(EngineConnection):
37
42
  def __init__(self, connection: Connection):
38
43
  self.connection = connection
39
44
 
40
- def execute(self, statement: str) -> SqlAlchemyResult:
41
- return SqlAlchemyResult(self.connection.execute(statement))
45
+ def execute(
46
+ self, statement: str, parameters: Any | None = None
47
+ ) -> SqlAlchemyResult:
48
+ return SqlAlchemyResult(self.connection.execute(statement, parameters))
42
49
 
43
50
 
44
51
  class SqlAlchemyEngine(ExecutionEngine):
trilogy/executor.py CHANGED
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from typing import Any, Generator, List, Optional, Protocol
5
5
 
6
6
  from sqlalchemy import text
7
- from sqlalchemy.engine import CursorResult, Engine
7
+ from sqlalchemy.engine import CursorResult
8
8
 
9
9
  from trilogy.constants import logger
10
10
  from trilogy.core.enums import FunctionType, Granularity, IOType
@@ -33,6 +33,7 @@ from trilogy.core.statements.execute import (
33
33
  )
34
34
  from trilogy.dialect.base import BaseDialect
35
35
  from trilogy.dialect.enums import Dialects
36
+ from trilogy.engine import ExecutionEngine
36
37
  from trilogy.hooks.base_hook import BaseHook
37
38
  from trilogy.parser import parse_text
38
39
 
@@ -71,7 +72,7 @@ class Executor(object):
71
72
  def __init__(
72
73
  self,
73
74
  dialect: Dialects,
74
- engine: Engine,
75
+ engine: ExecutionEngine,
75
76
  environment: Optional[Environment] = None,
76
77
  hooks: List[BaseHook] | None = None,
77
78
  ):
@@ -109,9 +110,16 @@ class Executor(object):
109
110
  from trilogy.dialect.snowflake import SnowflakeDialect
110
111
 
111
112
  self.generator = SnowflakeDialect()
113
+ elif self.dialect == Dialects.DATAFRAME:
114
+ from trilogy.dialect.dataframe import DataframeDialect
115
+
116
+ self.generator = DataframeDialect()
112
117
  else:
113
118
  raise ValueError(f"Unsupported dialect {self.dialect}")
114
119
  self.connection = self.engine.connect()
120
+ # TODO: make generic
121
+ if self.dialect == Dialects.DATAFRAME:
122
+ self.engine.setup(self.environment, self.connection)
115
123
 
116
124
  def execute_statement(self, statement) -> Optional[CursorResult]:
117
125
  if not isinstance(