pandas-query-sql 0.8.1__tar.gz → 0.8.3.dev0__tar.gz
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.
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/PKG-INFO +10 -15
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/pyproject.toml +35 -50
- pandas_query_sql-0.8.3.dev0/pyproject.toml.orig +74 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/sqldf.py +22 -23
- pandas_query_sql-0.8.1/AUTHORS.md +0 -11
- pandas_query_sql-0.8.1/tests/__init__.py +0 -0
- pandas_query_sql-0.8.1/tests/pandasql/__init__.py +0 -0
- pandas_query_sql-0.8.1/tests/pandasql/test_pandasql.py +0 -335
- pandas_query_sql-0.8.1/tests/pandasql/test_utils.py +0 -46
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/LICENSE.txt +0 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/README.md +0 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/__init__.py +0 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/data/births.csv +0 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/data/births_by_month.csv +0 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/data/meat.csv +0 -0
- {pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/py.typed +0 -0
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pandas_query_sql
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.3.dev0
|
|
4
4
|
Summary: sqldf for pandas
|
|
5
|
-
Author
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Requires-Python: >=3.8
|
|
5
|
+
Author: Alexander Plavin, Greg Lamp, Michael Dempsey
|
|
6
|
+
Author-email: Alexander Plavin <alexander@plav.in>, Greg Lamp <lamp.greg@gmail.com>, Michael Dempsey <bluestealth@bluestealth.pw>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE.txt
|
|
10
9
|
Requires-Dist: numpy
|
|
11
10
|
Requires-Dist: packaging
|
|
12
11
|
Requires-Dist: pandas
|
|
13
12
|
Requires-Dist: sqlalchemy
|
|
14
|
-
Requires-Dist: psycopg2; extra ==
|
|
15
|
-
Requires-Dist: psycopg2-binary; extra ==
|
|
16
|
-
Requires-
|
|
17
|
-
|
|
18
|
-
Requires-Dist: pytest; extra == "testing"
|
|
19
|
-
Requires-Dist: psycopg2-binary; extra == "testing"
|
|
13
|
+
Requires-Dist: psycopg2 ; extra == 'postgresql'
|
|
14
|
+
Requires-Dist: psycopg2-binary ; extra == 'postgresql-binary'
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Project-URL: Homepage, https://github.com/bluestealth/pandasql/
|
|
20
17
|
Provides-Extra: postgresql
|
|
21
18
|
Provides-Extra: postgresql-binary
|
|
22
|
-
Provides-Extra: linters
|
|
23
|
-
Provides-Extra: testing
|
|
24
19
|
Description-Content-Type: text/markdown
|
|
25
20
|
|
|
26
21
|
pandasql
|
|
@@ -1,77 +1,65 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pandas_query_sql"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.3.dev0"
|
|
4
4
|
description = "sqldf for pandas"
|
|
5
|
-
authors = [
|
|
6
|
-
{ name = "Alexander Plavin", email = "alexander@plav.in" },
|
|
7
|
-
{ name = "Greg Lamp", email = "lamp.greg@gmail.com" },
|
|
8
|
-
{ name = "Michael Dempsey", email = "bluestealth@bluestealth.pw" },
|
|
9
|
-
]
|
|
10
5
|
dependencies = [
|
|
11
6
|
"numpy",
|
|
12
7
|
"packaging",
|
|
13
8
|
"pandas",
|
|
14
9
|
"sqlalchemy",
|
|
15
10
|
]
|
|
16
|
-
requires-python = ">=3.
|
|
11
|
+
requires-python = ">=3.11"
|
|
17
12
|
readme = "README.md"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
license = "MIT"
|
|
14
|
+
license-files = ["LICENSE.txt"]
|
|
15
|
+
|
|
16
|
+
[[project.authors]]
|
|
17
|
+
name = "Alexander Plavin"
|
|
18
|
+
email = "alexander@plav.in"
|
|
21
19
|
|
|
22
|
-
[project.
|
|
23
|
-
|
|
20
|
+
[[project.authors]]
|
|
21
|
+
name = "Greg Lamp"
|
|
22
|
+
email = "lamp.greg@gmail.com"
|
|
23
|
+
|
|
24
|
+
[[project.authors]]
|
|
25
|
+
name = "Michael Dempsey"
|
|
26
|
+
email = "bluestealth@bluestealth.pw"
|
|
24
27
|
|
|
25
28
|
[project.urls]
|
|
26
29
|
Homepage = "https://github.com/bluestealth/pandasql/"
|
|
27
30
|
|
|
28
31
|
[project.optional-dependencies]
|
|
29
|
-
postgresql = [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
]
|
|
35
|
-
linters = [
|
|
36
|
-
"black",
|
|
32
|
+
postgresql = ["psycopg2"]
|
|
33
|
+
postgresql-binary = ["psycopg2-binary"]
|
|
34
|
+
|
|
35
|
+
[dependency-groups]
|
|
36
|
+
dev = [
|
|
37
37
|
"ruff",
|
|
38
|
-
]
|
|
39
|
-
testing = [
|
|
40
38
|
"pytest",
|
|
41
39
|
"psycopg2-binary",
|
|
40
|
+
"testcontainers[postgres]",
|
|
41
|
+
"ty",
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
[build-system]
|
|
45
|
-
requires = [
|
|
46
|
-
|
|
47
|
-
]
|
|
48
|
-
build-backend = "pdm.backend"
|
|
49
|
-
|
|
50
|
-
[tool.pdm]
|
|
51
|
-
distribution = true
|
|
45
|
+
requires = ["uv_build>=0.13.5,<0.14"]
|
|
46
|
+
build-backend = "uv_build"
|
|
52
47
|
|
|
53
|
-
[tool.
|
|
54
|
-
|
|
48
|
+
[tool.uv.build-backend]
|
|
49
|
+
module-name = "pandasql"
|
|
55
50
|
|
|
56
|
-
[tool.
|
|
57
|
-
|
|
58
|
-
"data/births.csv",
|
|
59
|
-
"data/birth_by_months.csv",
|
|
60
|
-
"data/meat.csv",
|
|
61
|
-
"py.typed",
|
|
62
|
-
]
|
|
51
|
+
[tool.ty.src]
|
|
52
|
+
exclude = ["examples/**"]
|
|
63
53
|
|
|
64
54
|
[tool.ruff]
|
|
65
55
|
line-length = 120
|
|
66
|
-
target-version = "
|
|
56
|
+
target-version = "py311"
|
|
67
57
|
|
|
68
58
|
[tool.ruff.lint]
|
|
69
|
-
select = [
|
|
70
|
-
"ALL",
|
|
71
|
-
]
|
|
59
|
+
select = ["ALL"]
|
|
72
60
|
ignore = [
|
|
73
|
-
"
|
|
74
|
-
"
|
|
61
|
+
"COM812",
|
|
62
|
+
"CPY001",
|
|
75
63
|
"FBT001",
|
|
76
64
|
"FBT002",
|
|
77
65
|
"TRY003",
|
|
@@ -92,21 +80,18 @@ max-complexity = 14
|
|
|
92
80
|
convention = "google"
|
|
93
81
|
|
|
94
82
|
[tool.ruff.lint.per-file-ignores]
|
|
95
|
-
"src/pandasql/__init__.py" = [
|
|
96
|
-
"D",
|
|
97
|
-
]
|
|
83
|
+
"src/pandasql/__init__.py" = ["D"]
|
|
98
84
|
"examples/**/*.py" = [
|
|
99
85
|
"D",
|
|
100
86
|
"INP001",
|
|
101
87
|
]
|
|
102
88
|
"tests/**/*.py" = [
|
|
103
89
|
"D",
|
|
90
|
+
"BLE001",
|
|
104
91
|
]
|
|
105
92
|
|
|
106
93
|
[tool.ruff.lint.extend-per-file-ignores]
|
|
107
|
-
"examples/**/*.py" = [
|
|
108
|
-
"T",
|
|
109
|
-
]
|
|
94
|
+
"examples/**/*.py" = ["T"]
|
|
110
95
|
"tests/**/*.py" = [
|
|
111
96
|
"T",
|
|
112
97
|
"S101",
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pandas_query_sql"
|
|
3
|
+
version = "0.8.3.dev0"
|
|
4
|
+
description = "sqldf for pandas"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Alexander Plavin", email = "alexander@plav.in"},
|
|
7
|
+
{name = "Greg Lamp", email= "lamp.greg@gmail.com"},
|
|
8
|
+
{name = "Michael Dempsey", email = "bluestealth@bluestealth.pw"},
|
|
9
|
+
]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"numpy",
|
|
12
|
+
"packaging",
|
|
13
|
+
"pandas",
|
|
14
|
+
"sqlalchemy",
|
|
15
|
+
]
|
|
16
|
+
requires-python = ">=3.11"
|
|
17
|
+
readme = "README.md"
|
|
18
|
+
license = "MIT"
|
|
19
|
+
license-files = ["LICENSE.txt"]
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Homepage = "https://github.com/bluestealth/pandasql/"
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
postgresql = [
|
|
26
|
+
"psycopg2",
|
|
27
|
+
]
|
|
28
|
+
postgresql-binary = [
|
|
29
|
+
"psycopg2-binary",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
dev = [
|
|
34
|
+
"ruff",
|
|
35
|
+
"pytest",
|
|
36
|
+
"psycopg2-binary",
|
|
37
|
+
"testcontainers[postgres]",
|
|
38
|
+
'ty',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["uv_build>=0.13.5,<0.14"]
|
|
43
|
+
build-backend = "uv_build"
|
|
44
|
+
|
|
45
|
+
[tool.uv.build-backend]
|
|
46
|
+
module-name = "pandasql"
|
|
47
|
+
|
|
48
|
+
[tool.ty.src]
|
|
49
|
+
exclude = ["examples/**"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 120
|
|
53
|
+
target-version = 'py311'
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
select = ['ALL']
|
|
57
|
+
ignore = ['COM812', "CPY001", 'FBT001', 'FBT002', 'TRY003', 'EM101', 'PLR2004', 'N818']
|
|
58
|
+
flake8-quotes = {}
|
|
59
|
+
isort = { known-first-party = [] }
|
|
60
|
+
mccabe = { max-complexity = 14 }
|
|
61
|
+
pydocstyle = { convention = 'google' }
|
|
62
|
+
|
|
63
|
+
[tool.ruff.format]
|
|
64
|
+
quote-style = "double"
|
|
65
|
+
indent-style = "space"
|
|
66
|
+
|
|
67
|
+
[tool.ruff.lint.per-file-ignores]
|
|
68
|
+
'src/pandasql/__init__.py' = ['D']
|
|
69
|
+
"examples/**/*.py" = ['D', 'INP001']
|
|
70
|
+
"tests/**/*.py" = ['D', 'BLE001']
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.extend-per-file-ignores]
|
|
73
|
+
"examples/**/*.py" = ['T']
|
|
74
|
+
"tests/**/*.py" = ['T', 'S101', 'PD901', 'PLR']
|
|
@@ -4,17 +4,20 @@ import importlib.metadata
|
|
|
4
4
|
import inspect
|
|
5
5
|
import re
|
|
6
6
|
import sqlite3
|
|
7
|
+
from collections.abc import Generator
|
|
7
8
|
from contextlib import contextmanager
|
|
8
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
9
10
|
from warnings import catch_warnings, filterwarnings
|
|
10
11
|
|
|
11
12
|
import packaging.version
|
|
12
13
|
from pandas import DataFrame
|
|
14
|
+
from pandas.errors import DatabaseError as PandasDatabaseError
|
|
13
15
|
from pandas.io.sql import read_sql_query
|
|
14
16
|
from sqlalchemy import create_engine # pyright: ignore[reportUnknownVariableType]
|
|
15
17
|
from sqlalchemy.engine import Connection, Engine
|
|
16
18
|
from sqlalchemy.event import listen # pyright: ignore[reportUnknownVariableType]
|
|
17
|
-
from sqlalchemy.exc import DatabaseError
|
|
19
|
+
from sqlalchemy.exc import DatabaseError as SQLAlchemyDatabaseError
|
|
20
|
+
from sqlalchemy.exc import ResourceClosedError
|
|
18
21
|
from sqlalchemy.pool import NullPool
|
|
19
22
|
from sqlalchemy.pool.base import (
|
|
20
23
|
_ConnectionRecord, # pyright: ignore[reportPrivateUsage]
|
|
@@ -40,7 +43,7 @@ class PandaSQL:
|
|
|
40
43
|
loaded_tables: set of currently loaded table names
|
|
41
44
|
"""
|
|
42
45
|
|
|
43
|
-
def __init__(self, db_uri:
|
|
46
|
+
def __init__(self, db_uri: str | None, persist: bool = False) -> None:
|
|
44
47
|
"""Initialize with a specific database.
|
|
45
48
|
|
|
46
49
|
:param db_uri: SQLAlchemy-compatible database URI.
|
|
@@ -61,7 +64,7 @@ class PandaSQL:
|
|
|
61
64
|
)
|
|
62
65
|
|
|
63
66
|
self.persist = persist
|
|
64
|
-
self.loaded_tables:
|
|
67
|
+
self.loaded_tables: set[str] = set()
|
|
65
68
|
if self.persist:
|
|
66
69
|
self._conn = self.engine.connect()
|
|
67
70
|
self._init_connection(self._conn)
|
|
@@ -69,8 +72,8 @@ class PandaSQL:
|
|
|
69
72
|
def __call__(
|
|
70
73
|
self,
|
|
71
74
|
query: str,
|
|
72
|
-
env:
|
|
73
|
-
) ->
|
|
75
|
+
env: dict[str, Any] | None = None,
|
|
76
|
+
) -> DataFrame | None:
|
|
74
77
|
"""Execute the SQL query.
|
|
75
78
|
|
|
76
79
|
Automatically creates tables mentioned in the query from dataframes before executing.
|
|
@@ -96,17 +99,17 @@ class PandaSQL:
|
|
|
96
99
|
|
|
97
100
|
try:
|
|
98
101
|
result = read_sql_query(sql=query, con=conn)
|
|
99
|
-
except DatabaseError as ex:
|
|
100
|
-
raise PandaSQLException(ex) from ex
|
|
101
102
|
except ResourceClosedError:
|
|
102
103
|
# query returns nothing
|
|
103
104
|
result = None
|
|
105
|
+
except (SQLAlchemyDatabaseError, PandasDatabaseError) as ex:
|
|
106
|
+
raise PandaSQLException(ex) from ex
|
|
104
107
|
|
|
105
108
|
return result
|
|
106
109
|
|
|
107
110
|
@property
|
|
108
111
|
@contextmanager
|
|
109
|
-
def conn(self) ->
|
|
112
|
+
def conn(self) -> Generator[Connection]:
|
|
110
113
|
"""Context Manager for database connection."""
|
|
111
114
|
if self.persist:
|
|
112
115
|
# the connection is created in __init__, so just return it
|
|
@@ -125,13 +128,13 @@ class PandaSQL:
|
|
|
125
128
|
def _init_connection(self, conn: Connection) -> None:
|
|
126
129
|
if self.engine.name == "postgresql":
|
|
127
130
|
if SQLALCHEMY_MAJOR_VERSION >= 2:
|
|
128
|
-
from sqlalchemy import text
|
|
131
|
+
from sqlalchemy import text # noqa: PLC0415
|
|
129
132
|
|
|
130
133
|
conn.execute( # pyright: ignore[reportUnknownMemberType]
|
|
131
134
|
statement=text("set search_path to pg_temp"),
|
|
132
135
|
)
|
|
133
136
|
else:
|
|
134
|
-
conn.execute( #
|
|
137
|
+
conn.execute( # ty: ignore[no-matching-overload]
|
|
135
138
|
statement="set search_path to pg_temp",
|
|
136
139
|
)
|
|
137
140
|
|
|
@@ -143,26 +146,22 @@ class PandaSQL:
|
|
|
143
146
|
dbapi_conn.text_factory = str
|
|
144
147
|
|
|
145
148
|
|
|
146
|
-
def get_outer_frame_variables() ->
|
|
149
|
+
def get_outer_frame_variables() -> dict[str, Any]:
|
|
147
150
|
"""Get a dict of local and global variables of the first outer frame from another file."""
|
|
148
151
|
frame = inspect.currentframe()
|
|
149
|
-
variables:
|
|
152
|
+
variables: dict[str, Any] = {}
|
|
150
153
|
|
|
151
154
|
if not frame:
|
|
152
155
|
return variables
|
|
153
156
|
|
|
154
157
|
cur_filename = inspect.getframeinfo(frame).filename
|
|
155
|
-
outer_frame = next(
|
|
156
|
-
f
|
|
157
|
-
for f in inspect.getouterframes(inspect.currentframe())
|
|
158
|
-
if f.filename != cur_filename
|
|
159
|
-
)
|
|
158
|
+
outer_frame = next(f for f in inspect.getouterframes(inspect.currentframe()) if f.filename != cur_filename)
|
|
160
159
|
variables.update(outer_frame.frame.f_globals)
|
|
161
160
|
variables.update(outer_frame.frame.f_locals)
|
|
162
161
|
return variables
|
|
163
162
|
|
|
164
163
|
|
|
165
|
-
def extract_table_names(query: str) ->
|
|
164
|
+
def extract_table_names(query: str) -> set[str]:
|
|
166
165
|
"""Extract table names from an SQL query."""
|
|
167
166
|
# a good old fashioned regex. turns out this worked better than actually parsing the code
|
|
168
167
|
tables_blocks = re.findall(
|
|
@@ -176,7 +175,7 @@ def extract_table_names(query: str) -> Set[str]:
|
|
|
176
175
|
def write_table(
|
|
177
176
|
df: DataFrame,
|
|
178
177
|
tablename: str,
|
|
179
|
-
conn:
|
|
178
|
+
conn: Engine | Connection | sqlite3.Connection,
|
|
180
179
|
) -> None:
|
|
181
180
|
"""Write Pandas DataFrame to SQL Table.
|
|
182
181
|
|
|
@@ -205,9 +204,9 @@ def write_table(
|
|
|
205
204
|
|
|
206
205
|
def sqldf(
|
|
207
206
|
query: str,
|
|
208
|
-
env:
|
|
209
|
-
db_uri:
|
|
210
|
-
) ->
|
|
207
|
+
env: dict[str, Any] | None = None,
|
|
208
|
+
db_uri: str | None = None,
|
|
209
|
+
) -> DataFrame | None:
|
|
211
210
|
"""Query pandas data frames using sql syntax.
|
|
212
211
|
|
|
213
212
|
This function is meant for backward compatibility only. New users are encouraged to use the PandaSQL class.
|
|
File without changes
|
|
File without changes
|
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
import importlib.metadata
|
|
2
|
-
import string
|
|
3
|
-
from typing import Dict
|
|
4
|
-
|
|
5
|
-
import packaging.version
|
|
6
|
-
import pandas as pd
|
|
7
|
-
import pandas.testing as pdtest
|
|
8
|
-
import pytest
|
|
9
|
-
|
|
10
|
-
from pandasql import PandaSQL, PandaSQLException, load_meat, sqldf
|
|
11
|
-
|
|
12
|
-
PANDAS_MAJOR_VERSION = packaging.version.Version(
|
|
13
|
-
importlib.metadata.version("pandas"),
|
|
14
|
-
).major
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@pytest.fixture()
|
|
18
|
-
def db_uris() -> Dict[str, str]:
|
|
19
|
-
return {
|
|
20
|
-
"sqlite": "sqlite:///:memory:",
|
|
21
|
-
"postgres": "postgresql://postgres@localhost/",
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@pytest.fixture(params=["sqlite", "postgres"])
|
|
26
|
-
def db_flavor(request: pytest.FixtureRequest) -> str:
|
|
27
|
-
return str(request.param)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@pytest.fixture()
|
|
31
|
-
def db_uri(db_uris: Dict[str, str], db_flavor: str) -> str:
|
|
32
|
-
return db_uris[db_flavor]
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@pytest.fixture(params=[False, True])
|
|
36
|
-
def pdsql(db_uri: str, request: pytest.FixtureRequest) -> PandaSQL:
|
|
37
|
-
return PandaSQL(db_uri, persist=request.param)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def test_select_legacy(db_uri: str) -> None:
|
|
41
|
-
df = pd.DataFrame(
|
|
42
|
-
{
|
|
43
|
-
"letter_pos": range(len(string.ascii_letters)),
|
|
44
|
-
"l2": list(string.ascii_letters),
|
|
45
|
-
},
|
|
46
|
-
)
|
|
47
|
-
result = sqldf("SELECT * FROM df LIMIT 10", db_uri=db_uri)
|
|
48
|
-
|
|
49
|
-
assert result is not None
|
|
50
|
-
assert len(result) == 10
|
|
51
|
-
pdtest.assert_frame_equal(df.head(10), result)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def test_select(pdsql: PandaSQL) -> None:
|
|
55
|
-
df = pd.DataFrame(
|
|
56
|
-
{
|
|
57
|
-
"letter_pos": range(len(string.ascii_letters)),
|
|
58
|
-
"l2": list(string.ascii_letters),
|
|
59
|
-
},
|
|
60
|
-
)
|
|
61
|
-
result = pdsql("SELECT * FROM df LIMIT 10")
|
|
62
|
-
|
|
63
|
-
assert result is not None
|
|
64
|
-
assert len(result) == 10
|
|
65
|
-
pdtest.assert_frame_equal(df.head(10), result)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def test_join(pdsql: PandaSQL) -> None:
|
|
69
|
-
df = pd.DataFrame(
|
|
70
|
-
{
|
|
71
|
-
"letter_pos": range(len(string.ascii_letters)),
|
|
72
|
-
"l2": list(string.ascii_letters),
|
|
73
|
-
},
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
df2 = pd.DataFrame(
|
|
77
|
-
{
|
|
78
|
-
"letter_pos": range(len(string.ascii_letters)),
|
|
79
|
-
"letter": list(string.ascii_letters),
|
|
80
|
-
},
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
result = pdsql(
|
|
84
|
-
"SELECT a.*, b.letter FROM df a INNER JOIN df2 b ON a.l2 = b.letter LIMIT 20",
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
assert result is not None
|
|
88
|
-
assert len(result) == 20
|
|
89
|
-
pdtest.assert_frame_equal(
|
|
90
|
-
df[["letter_pos", "l2"]].head(20),
|
|
91
|
-
result[["letter_pos", "l2"]],
|
|
92
|
-
)
|
|
93
|
-
pdtest.assert_frame_equal(df2[["letter"]].head(20), result[["letter"]])
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def test_query_with_spacing(pdsql: PandaSQL) -> None:
|
|
97
|
-
df = pd.DataFrame(
|
|
98
|
-
{
|
|
99
|
-
"letter_pos": range(len(string.ascii_letters)),
|
|
100
|
-
"l2": list(string.ascii_letters),
|
|
101
|
-
},
|
|
102
|
-
)
|
|
103
|
-
assert df is not None
|
|
104
|
-
|
|
105
|
-
df2 = pd.DataFrame(
|
|
106
|
-
{
|
|
107
|
-
"letter_pos": range(len(string.ascii_letters)),
|
|
108
|
-
"letter": list(string.ascii_letters),
|
|
109
|
-
},
|
|
110
|
-
)
|
|
111
|
-
assert df2 is not None
|
|
112
|
-
|
|
113
|
-
expected = pdsql(
|
|
114
|
-
"SELECT a.*, b.letter FROM df a INNER JOIN df2 b ON a.l2 = b.letter LIMIT 20",
|
|
115
|
-
)
|
|
116
|
-
assert expected is not None
|
|
117
|
-
|
|
118
|
-
q = """
|
|
119
|
-
SELECT
|
|
120
|
-
a.*
|
|
121
|
-
, b.letter
|
|
122
|
-
FROM
|
|
123
|
-
df a
|
|
124
|
-
INNER JOIN
|
|
125
|
-
df2 b
|
|
126
|
-
on a.l2 = b.letter
|
|
127
|
-
LIMIT 20
|
|
128
|
-
"""
|
|
129
|
-
result = pdsql(q)
|
|
130
|
-
assert result is not None
|
|
131
|
-
assert len(result) == 20
|
|
132
|
-
pdtest.assert_frame_equal(expected, result)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def test_subquery(pdsql: PandaSQL) -> None:
|
|
136
|
-
kermit = pd.DataFrame({"x": range(10)})
|
|
137
|
-
result = pdsql("SELECT * FROM (SELECT * FROM kermit) tbl LIMIT 2")
|
|
138
|
-
assert result is not None
|
|
139
|
-
pdtest.assert_frame_equal(kermit.head(2), result)
|
|
140
|
-
assert len(result) == 2
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def test_in(pdsql: PandaSQL) -> None:
|
|
144
|
-
course_data = {
|
|
145
|
-
"coursecode": ["TM351", "TU100", "M269"],
|
|
146
|
-
"points": [30, 60, 30],
|
|
147
|
-
"level": ["3", "1", "2"],
|
|
148
|
-
}
|
|
149
|
-
course_df = pd.DataFrame(course_data)
|
|
150
|
-
assert not course_df.empty
|
|
151
|
-
|
|
152
|
-
result = pdsql("SELECT * FROM course_df WHERE coursecode IN ( 'TM351', 'TU100' )")
|
|
153
|
-
assert result is not None
|
|
154
|
-
assert len(result) == 2
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def test_in_with_subquery(pdsql: PandaSQL) -> None:
|
|
158
|
-
program_data = {
|
|
159
|
-
"coursecode": [
|
|
160
|
-
"TM351",
|
|
161
|
-
"TM351",
|
|
162
|
-
"TM351",
|
|
163
|
-
"TU100",
|
|
164
|
-
"TU100",
|
|
165
|
-
"TU100",
|
|
166
|
-
"M269",
|
|
167
|
-
"M269",
|
|
168
|
-
"M269",
|
|
169
|
-
],
|
|
170
|
-
"programCode": ["AB1", "AB2", "AB3", "AB1", "AB3", "AB4", "AB3", "AB4", "AB5"],
|
|
171
|
-
}
|
|
172
|
-
program_df = pd.DataFrame(program_data)
|
|
173
|
-
assert not program_df.empty
|
|
174
|
-
|
|
175
|
-
course_data = {
|
|
176
|
-
"coursecode": ["TM351", "TU100", "M269"],
|
|
177
|
-
"points": [30, 60, 30],
|
|
178
|
-
"level": ["3", "1", "2"],
|
|
179
|
-
}
|
|
180
|
-
course_df = pd.DataFrame(course_data)
|
|
181
|
-
assert not course_df.empty
|
|
182
|
-
|
|
183
|
-
result = pdsql(
|
|
184
|
-
"SELECT * FROM course_df WHERE coursecode IN ( SELECT DISTINCT coursecode FROM program_df )",
|
|
185
|
-
)
|
|
186
|
-
assert result is not None
|
|
187
|
-
assert len(result) == 3
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def test_datetime_query(pdsql: PandaSQL, db_flavor: str) -> None:
|
|
191
|
-
meat = load_meat()
|
|
192
|
-
expected = meat[meat["date"] >= "2012-01-01"].reset_index(drop=True)
|
|
193
|
-
result = pdsql("SELECT * FROM meat WHERE date >= '2012-01-01'")
|
|
194
|
-
assert result is not None
|
|
195
|
-
|
|
196
|
-
if db_flavor == "sqlite":
|
|
197
|
-
# sqlite uses strings instead of datetimes
|
|
198
|
-
pdtest.assert_frame_equal(
|
|
199
|
-
expected.drop(labels="date", axis="columns"),
|
|
200
|
-
result.drop(labels="date", axis="columns"),
|
|
201
|
-
)
|
|
202
|
-
else:
|
|
203
|
-
pdtest.assert_frame_equal(expected, result)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
def test_returning_single(pdsql: PandaSQL) -> None:
|
|
207
|
-
meat = load_meat()
|
|
208
|
-
result = pdsql("SELECT beef FROM meat LIMIT 10")
|
|
209
|
-
assert result is not None
|
|
210
|
-
assert len(result) == 10
|
|
211
|
-
pdtest.assert_frame_equal(meat[["beef"]].head(10), result)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
def test_name_index(pdsql: PandaSQL) -> None:
|
|
215
|
-
df = pd.DataFrame(
|
|
216
|
-
{
|
|
217
|
-
"index": range(len(string.ascii_letters)),
|
|
218
|
-
"level_0": range(len(string.ascii_letters)),
|
|
219
|
-
"level_1": range(len(string.ascii_letters)),
|
|
220
|
-
"letter": list(string.ascii_letters),
|
|
221
|
-
},
|
|
222
|
-
)
|
|
223
|
-
result = pdsql("SELECT * FROM df")
|
|
224
|
-
assert result is not None
|
|
225
|
-
pdtest.assert_frame_equal(df, result)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def test_nonexistent_table(pdsql: PandaSQL) -> None:
|
|
229
|
-
with pytest.raises(PandaSQLException):
|
|
230
|
-
pdsql("SELECT * FROM nosuchtablereally")
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
def test_system_tables(pdsql: PandaSQL, db_flavor: str) -> None:
|
|
234
|
-
if db_flavor == "sqlite":
|
|
235
|
-
# sqlite doesn't have information_schema
|
|
236
|
-
result = pdsql("SELECT * FROM sqlite_master")
|
|
237
|
-
else:
|
|
238
|
-
result = pdsql("SELECT * FROM information_schema.tables")
|
|
239
|
-
assert result is not None
|
|
240
|
-
assert len(result.columns) > 1
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
@pytest.mark.parametrize(
|
|
244
|
-
"db_flavor",
|
|
245
|
-
["postgres"],
|
|
246
|
-
) # sqlite doesn't support tables with no columns
|
|
247
|
-
def test_no_columns(pdsql: PandaSQL) -> None:
|
|
248
|
-
df = pd.DataFrame()
|
|
249
|
-
result = pdsql("SELECT * FROM df")
|
|
250
|
-
assert result is not None
|
|
251
|
-
assert df.empty
|
|
252
|
-
assert result.empty
|
|
253
|
-
pdtest.assert_frame_equal(df, result, check_column_type=(PANDAS_MAJOR_VERSION < 2))
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def test_empty(pdsql: PandaSQL) -> None:
|
|
257
|
-
df = pd.DataFrame({"x": []})
|
|
258
|
-
result = pdsql("SELECT * FROM df")
|
|
259
|
-
assert result is not None
|
|
260
|
-
assert result.empty
|
|
261
|
-
pdtest.assert_index_equal(df.columns, result.columns)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
def test_noleak_legacy(db_uri: str) -> None:
|
|
265
|
-
df = pd.DataFrame({"x": [1]})
|
|
266
|
-
result = sqldf("SELECT * FROM df", db_uri=db_uri)
|
|
267
|
-
assert result is not None
|
|
268
|
-
pdtest.assert_frame_equal(df, result)
|
|
269
|
-
del df
|
|
270
|
-
with pytest.raises(PandaSQLException):
|
|
271
|
-
result = sqldf("SELECT * FROM df", db_uri=db_uri)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
@pytest.mark.parametrize("pdsql", [False], indirect=True)
|
|
275
|
-
def test_noleak_class(pdsql: PandaSQL) -> None:
|
|
276
|
-
df = pd.DataFrame({"x": [1]})
|
|
277
|
-
result = pdsql("SELECT * FROM df")
|
|
278
|
-
assert result is not None
|
|
279
|
-
pdtest.assert_frame_equal(df, result)
|
|
280
|
-
del df
|
|
281
|
-
with pytest.raises(PandaSQLException):
|
|
282
|
-
result = pdsql("SELECT * FROM df")
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
def test_same_query_noerr(pdsql: PandaSQL) -> None:
|
|
286
|
-
df = pd.DataFrame({"x": [1]})
|
|
287
|
-
result1 = pdsql("SELECT * FROM df")
|
|
288
|
-
assert result1 is not None
|
|
289
|
-
pdtest.assert_frame_equal(df, result1)
|
|
290
|
-
result2 = pdsql("SELECT * FROM df")
|
|
291
|
-
assert result2 is not None
|
|
292
|
-
pdtest.assert_frame_equal(result1, result2)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
@pytest.mark.parametrize("pdsql", [True], indirect=True)
|
|
296
|
-
def test_persistent(pdsql: PandaSQL) -> None:
|
|
297
|
-
df = pd.DataFrame({"x": [1]})
|
|
298
|
-
result1 = pdsql("SELECT * FROM df")
|
|
299
|
-
assert result1 is not None
|
|
300
|
-
pdtest.assert_frame_equal(df, result1)
|
|
301
|
-
|
|
302
|
-
del df
|
|
303
|
-
result2 = pdsql("SELECT * FROM df")
|
|
304
|
-
assert result2 is not None
|
|
305
|
-
pdtest.assert_frame_equal(result1, result2)
|
|
306
|
-
|
|
307
|
-
df = pd.DataFrame({"x": [1, 2]}) # will not have any effect
|
|
308
|
-
assert not df.empty
|
|
309
|
-
result3 = pdsql("SELECT * FROM df")
|
|
310
|
-
assert result3 is not None
|
|
311
|
-
pdtest.assert_frame_equal(result1, result3)
|
|
312
|
-
|
|
313
|
-
df1 = pd.DataFrame({"x": [1, 2, 3]})
|
|
314
|
-
result4 = pdsql("SELECT * FROM df1")
|
|
315
|
-
assert result4 is not None
|
|
316
|
-
pdtest.assert_frame_equal(df1, result4)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
def test_noreturn_query(pdsql: PandaSQL) -> None:
|
|
320
|
-
assert pdsql("CREATE TABLE tbl (col INTEGER)") is None
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
@pytest.mark.parametrize("pdsql", [False], indirect=True)
|
|
324
|
-
def test_no_sideeffect_leak(pdsql: PandaSQL) -> None:
|
|
325
|
-
pdsql("CREATE TABLE tbl (col INTEGER)")
|
|
326
|
-
with pytest.raises(PandaSQLException):
|
|
327
|
-
pdsql("SELECT * FROM tbl")
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
@pytest.mark.parametrize("pdsql", [True], indirect=True)
|
|
331
|
-
def test_sideeffect_persist(pdsql: PandaSQL) -> None:
|
|
332
|
-
pdsql("CREATE TABLE tbl (col INTEGER)")
|
|
333
|
-
result = pdsql("SELECT * FROM tbl")
|
|
334
|
-
assert result is not None
|
|
335
|
-
assert list(result.columns) == ["col"]
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
from typing import Set
|
|
2
|
-
|
|
3
|
-
import pytest
|
|
4
|
-
|
|
5
|
-
from pandasql.sqldf import extract_table_names, get_outer_frame_variables
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def test_get_vars() -> None:
|
|
9
|
-
var_a = 123
|
|
10
|
-
variable = {"a": "b", "c": "d"}
|
|
11
|
-
assert get_outer_frame_variables()["var_a"] == var_a
|
|
12
|
-
assert get_outer_frame_variables()["variable"] == variable
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@pytest.mark.parametrize(
|
|
16
|
-
("query", "expected"),
|
|
17
|
-
[
|
|
18
|
-
("", set()),
|
|
19
|
-
("SELECT * FROM dbtbl LIMIT 10", {"dbtbl"}),
|
|
20
|
-
("SELECT * from dbtbl LIMIT 10", {"dbtbl"}),
|
|
21
|
-
(
|
|
22
|
-
"SELECT * FROM dbtbl JOIN table_2 USING (a) LEFT JOIN mytable USING (b)",
|
|
23
|
-
{"dbtbl", "table_2", "mytable"},
|
|
24
|
-
),
|
|
25
|
-
(
|
|
26
|
-
"SELECT * FROM dbtbl, table_2, mytable JOIN another USING (a)",
|
|
27
|
-
{"dbtbl", "table_2", "mytable", "another"},
|
|
28
|
-
),
|
|
29
|
-
("SELECT * FROM dbtbl, table_2 JOIN dbtbl USING (a)", {"dbtbl", "table_2"}),
|
|
30
|
-
pytest.param(
|
|
31
|
-
"SELECT * FROM dbtbl WHERE col = 'Go and join us!'",
|
|
32
|
-
{"dbtbl"},
|
|
33
|
-
marks=pytest.mark.xfail,
|
|
34
|
-
),
|
|
35
|
-
(
|
|
36
|
-
"SELECT * FROM course_df WHERE coursecode IN ( SELECT DISTINCT coursecode FROM program_df )",
|
|
37
|
-
{"course_df", "program_df"},
|
|
38
|
-
),
|
|
39
|
-
(
|
|
40
|
-
"SELECT * FROM course_df, (SELECT coursecode FROM program_df) ssel",
|
|
41
|
-
{"course_df", "program_df"},
|
|
42
|
-
),
|
|
43
|
-
],
|
|
44
|
-
)
|
|
45
|
-
def test_extract_table_names(query: str, expected: Set[str]) -> None:
|
|
46
|
-
assert extract_table_names(query) == expected
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pandas_query_sql-0.8.1 → pandas_query_sql-0.8.3.dev0}/src/pandasql/data/births_by_month.csv
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|