started working on the sql part of the lib
This commit is contained in:
parent
bb3c92bfd8
commit
3ff2db97b6
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@ wheels/
|
|||||||
|
|
||||||
# Virtual environments
|
# Virtual environments
|
||||||
.venv
|
.venv
|
||||||
|
*.csv
|
||||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="postgres@192.168.1.44" uuid="6586f5ca-2b2d-4fdd-adc5-118bad076bfa">
|
||||||
|
<driver-ref>postgresql</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:postgresql://192.168.1.44:5432/postgres</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/sqldialects.xml
generated
Normal file
6
.idea/sqldialects.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="SqlDialectMappings">
|
||||||
|
<file url="file://$PROJECT_DIR$/query.sql" dialect="PostgreSQL" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
51
config.py
51
config.py
@ -1,18 +1,56 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseType(Enum):
|
||||||
|
PSQL = 1
|
||||||
|
ORCL = 2
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DatabaseConfig:
|
class DatabaseConfig:
|
||||||
_host: str
|
_host: str
|
||||||
|
_database_type: DatabaseType | None
|
||||||
|
_database_name: str
|
||||||
|
_database_ssid: str
|
||||||
|
_database_port: str
|
||||||
|
|
||||||
def __init__(self, config: dict):
|
def __init__(self, config: dict):
|
||||||
self._host = config["HOST"]
|
self._host = config["HOST"]
|
||||||
|
type = config["DATABASE_TYPE"]
|
||||||
|
|
||||||
|
match type:
|
||||||
|
case 'PSQL':
|
||||||
|
self._database_type = DatabaseType.PSQL
|
||||||
|
case 'ORCL':
|
||||||
|
self._database_type = DatabaseType.ORCL
|
||||||
|
case _:
|
||||||
|
self._database_type = None
|
||||||
|
|
||||||
|
self._database_name = config["DATABASE_NAME"]
|
||||||
|
self._database_ssid = config["DATABASE_SSID"]
|
||||||
|
self._database_port = config["DATABASE_PORT"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def host(self) -> str:
|
def host(self) -> str:
|
||||||
return self._host
|
return self._host
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> DatabaseType:
|
||||||
|
return self._database_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._database_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ssid(self) -> str:
|
||||||
|
return self._database_ssid
|
||||||
|
|
||||||
|
@property
|
||||||
|
def port(self) -> str:
|
||||||
|
return self._database_port
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class KeePassConfig:
|
class KeePassConfig:
|
||||||
@ -21,9 +59,9 @@ class KeePassConfig:
|
|||||||
_db_credentials_group: str
|
_db_credentials_group: str
|
||||||
|
|
||||||
def __init__(self, config: dict):
|
def __init__(self, config: dict):
|
||||||
self._path = config["PATH"]
|
self._path: str = config["PATH"]
|
||||||
self._db_credentials_name = config["DB_CREDENTIALS_NAME"]
|
self._db_credentials_name: str = config["DB_CREDENTIALS_NAME"]
|
||||||
self._db_credentials_group = config["DB_CREDENTIALS_GROUP"]
|
self._db_credentials_group: str = config["DB_CREDENTIALS_GROUP"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self) -> str:
|
def path(self) -> str:
|
||||||
@ -42,11 +80,12 @@ class Config:
|
|||||||
_config: dict
|
_config: dict
|
||||||
_kee_pass_config: KeePassConfig
|
_kee_pass_config: KeePassConfig
|
||||||
_database_config: DatabaseConfig
|
_database_config: DatabaseConfig
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
with open('config.toml', 'r') as f:
|
with open('config.toml', 'r') as f:
|
||||||
self._config = toml.load(f)
|
self._config: dict = toml.load(f)
|
||||||
self._kee_pass_config = KeePassConfig(self._config['keepass'])
|
self._kee_pass_config: KeePassConfig = KeePassConfig(self._config['keepass'])
|
||||||
self._database_config = DatabaseConfig(self._config['database'])
|
self._database_config: DatabaseConfig = DatabaseConfig(self._config['database'])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kee_pass(self) -> KeePassConfig:
|
def kee_pass(self) -> KeePassConfig:
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
[database]
|
[database]
|
||||||
HOST = "192.168.1.44"
|
HOST = "192.168.1.44"
|
||||||
|
DATABASE_TYPE = "PSQL"
|
||||||
|
DATABASE_NAME = "op_test"
|
||||||
|
DATABASE_SSID = "XE"
|
||||||
|
DATABASE_PORT = "5432"
|
||||||
|
|
||||||
[keepass]
|
[keepass]
|
||||||
PATH = "/Users/frederik/Passwords.kdbx"
|
PATH = "/Users/frederik/Passwords.kdbx"
|
||||||
|
|||||||
78
db_adapter.py
Normal file
78
db_adapter.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import sqlalchemy as sq
|
||||||
|
import sqlparse
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from config import DatabaseConfig, DatabaseType
|
||||||
|
from keepass import KeePass
|
||||||
|
|
||||||
|
|
||||||
|
def _read_and_sql_file_and_strip_for_comments(filename: str):
|
||||||
|
query: str
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
query = f.read()
|
||||||
|
query = sqlparse.format(query, strip_comments=True)
|
||||||
|
return query
|
||||||
|
|
||||||
|
|
||||||
|
class DBAdapter:
|
||||||
|
_engine: sq.Engine
|
||||||
|
_database_config: DatabaseConfig
|
||||||
|
def __init__(self, keepass: KeePass, database_config: DatabaseConfig):
|
||||||
|
self._database_config = database_config
|
||||||
|
connection_string: str
|
||||||
|
keepass_entry = keepass.get_db_credentials()
|
||||||
|
|
||||||
|
match self._database_config.type:
|
||||||
|
case DatabaseType.PSQL:
|
||||||
|
connection_string = f'postgresql+pg8000://{keepass_entry.name}:{keepass_entry.password}@{self._database_config.host}/{self._database_config.name}'
|
||||||
|
case DatabaseType.ORCL:
|
||||||
|
connection_string = f'oracle:{keepass_entry.name}:{keepass_entry.password}@{self._database_config.host}:{self._database_config.port}:{self._database_config.ssid}'
|
||||||
|
case _:
|
||||||
|
raise Exception(f'Database type {database_config.type} is not supported')
|
||||||
|
|
||||||
|
self._engine: sq.Engine = sq.create_engine(connection_string)
|
||||||
|
|
||||||
|
def _set_transaction_readonly(self, conn: sq.Connection):
|
||||||
|
if not conn.in_transaction():
|
||||||
|
raise Exception('Connection is not in a transaction')
|
||||||
|
|
||||||
|
match self._database_config.type:
|
||||||
|
case DatabaseType.PSQL:
|
||||||
|
conn.execute(sq.text('SET TRANSACTION READ ONLY'))
|
||||||
|
case DatabaseType.ORCL:
|
||||||
|
conn.execute(sq.text('SET TRANSACTION READ ONLY'))
|
||||||
|
case _:
|
||||||
|
raise Exception(f'Database type {self.database_config.type} is not supported for readonly transactions')
|
||||||
|
def run_query(self, query: str, read_only = True) -> sq.CursorResult:
|
||||||
|
result: sq.CursorResult
|
||||||
|
with self._engine.connect() as conn:
|
||||||
|
with conn.begin():
|
||||||
|
try:
|
||||||
|
if read_only:
|
||||||
|
self._set_transaction_readonly(conn)
|
||||||
|
result = conn.execute(sq.text(query))
|
||||||
|
conn.commit()
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise e
|
||||||
|
return result
|
||||||
|
|
||||||
|
def run_sql_file_one_statement(self, filename: str = "query.sql", read_only = True) -> sq.CursorResult:
|
||||||
|
query = _read_and_sql_file_and_strip_for_comments(filename)
|
||||||
|
return self.run_query(query, read_only)
|
||||||
|
|
||||||
|
def run_sql_file_export_to_csv(self, input_name: str = "query.sql", output_name: str = "export.csv", read_only = True):
|
||||||
|
result: pd.DataFrame
|
||||||
|
query = _read_and_sql_file_and_strip_for_comments(input_name)
|
||||||
|
|
||||||
|
with self._engine.connect() as conn:
|
||||||
|
with conn.begin():
|
||||||
|
try:
|
||||||
|
if read_only:
|
||||||
|
self._set_transaction_readonly(conn)
|
||||||
|
result = pd.read_sql(query, conn)
|
||||||
|
conn.commit()
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
raise e
|
||||||
|
result.to_csv(output_name, index=False, sep=';')
|
||||||
35
keepass.py
35
keepass.py
@ -1,16 +1,31 @@
|
|||||||
from pykeepass import PyKeePass
|
from pykeepass import PyKeePass
|
||||||
from config import KeePassConfig
|
from config import KeePassConfig
|
||||||
import getpass
|
import getpass
|
||||||
class KeePass:
|
from dataclasses import dataclass
|
||||||
_kee_pass: PyKeePass
|
|
||||||
_kee_pass_config: KeePassConfig
|
|
||||||
_password: str
|
|
||||||
def __init__(self, config: KeePassConfig):
|
|
||||||
self._kee_pass_config = config
|
|
||||||
self._password = getpass.getpass(f'KeePass password for {config.path}: ')
|
|
||||||
self._kee_pass = PyKeePass(config.path, password=self._password)
|
|
||||||
|
|
||||||
def get_db_credentials(self):
|
@dataclass
|
||||||
|
class KeePassEntry:
|
||||||
|
def __init__(self, name: str, password: str):
|
||||||
|
self._name = name
|
||||||
|
self._password = password
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password(self) -> str:
|
||||||
|
return self._password
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class KeePass:
|
||||||
|
def __init__(self, config: KeePassConfig):
|
||||||
|
self._kee_pass_config: KeePassConfig = config
|
||||||
|
self._password: str = getpass.getpass(f'KeePass password for {config.path}: ')
|
||||||
|
self._kee_pass: PyKeePass = PyKeePass(config.path, password=self._password)
|
||||||
|
|
||||||
|
def get_db_credentials(self) -> KeePassEntry:
|
||||||
group = self._kee_pass
|
group = self._kee_pass
|
||||||
if self._kee_pass_config.db_credentials_group.strip() != "" and self._kee_pass_config.db_credentials_group.strip() is not None:
|
if self._kee_pass_config.db_credentials_group.strip() != "" and self._kee_pass_config.db_credentials_group.strip() is not None:
|
||||||
group = self._kee_pass.find_entries(name=self._kee_pass_config.db_credentials_name)
|
group = self._kee_pass.find_entries(name=self._kee_pass_config.db_credentials_name)
|
||||||
@ -23,4 +38,4 @@ class KeePass:
|
|||||||
if len(group) != 1:
|
if len(group) != 1:
|
||||||
raise Exception(f'Could not find password, found {len(group)} entries')
|
raise Exception(f'Could not find password, found {len(group)} entries')
|
||||||
|
|
||||||
return group[0].username, group[0].password
|
return KeePassEntry(group[0].username, group[0].password)
|
||||||
4
main.py
4
main.py
@ -1,4 +1,5 @@
|
|||||||
from config import Config, DatabaseConfig, KeePassConfig
|
from config import Config, DatabaseConfig, KeePassConfig
|
||||||
|
from db_adapter import DBAdapter
|
||||||
from keepass import KeePass
|
from keepass import KeePass
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
@ -7,3 +8,6 @@ print(config.database)
|
|||||||
|
|
||||||
keepass = KeePass(config.kee_pass)
|
keepass = KeePass(config.kee_pass)
|
||||||
print(keepass.get_db_credentials())
|
print(keepass.get_db_credentials())
|
||||||
|
|
||||||
|
db_adapter = DBAdapter(keepass, config.database)
|
||||||
|
db_adapter.run_sql_file_export_to_csv()
|
||||||
@ -5,8 +5,11 @@ description = "Add your description here"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.13"
|
requires-python = ">=3.13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cx-oracle>=8.3.0",
|
||||||
"pandas>=2.2.3",
|
"pandas>=2.2.3",
|
||||||
|
"pg8000>=1.31.2",
|
||||||
"pykeepass>=4.1.0.post1",
|
"pykeepass>=4.1.0.post1",
|
||||||
"sqlalchemy>=2.0.36",
|
"sqlalchemy>=2.0.36",
|
||||||
|
"sqlparse>=0.5.3",
|
||||||
"toml>=0.10.2",
|
"toml>=0.10.2",
|
||||||
]
|
]
|
||||||
|
|||||||
55
uv.lock
generated
55
uv.lock
generated
@ -34,6 +34,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104 },
|
{ url = "https://files.pythonhosted.org/packages/5a/e4/bf8034d25edaa495da3c8a3405627d2e35758e44ff6eaa7948092646fdcc/argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93", size = 53104 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asn1crypto"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cffi"
|
name = "cffi"
|
||||||
version = "1.17.1"
|
version = "1.17.1"
|
||||||
@ -65,22 +74,34 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b2/fb/08b3f4bf05da99aba8ffea52a558758def16e8516bc75ca94ff73587e7d3/construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30", size = 63020 },
|
{ url = "https://files.pythonhosted.org/packages/b2/fb/08b3f4bf05da99aba8ffea52a558758def16e8516bc75ca94ff73587e7d3/construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30", size = 63020 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cx-oracle"
|
||||||
|
version = "8.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e8/16/13c265afc984796fe38ee928733569b599cfd657245ddd1afad238b66656/cx_Oracle-8.3.0.tar.gz", hash = "sha256:3b2d215af4441463c97ea469b9cc307460739f89fdfa8ea222ea3518f1a424d9", size = 363886 }
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "database-interacter"
|
name = "database-interacter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
{ name = "cx-oracle" },
|
||||||
{ name = "pandas" },
|
{ name = "pandas" },
|
||||||
|
{ name = "pg8000" },
|
||||||
{ name = "pykeepass" },
|
{ name = "pykeepass" },
|
||||||
{ name = "sqlalchemy" },
|
{ name = "sqlalchemy" },
|
||||||
|
{ name = "sqlparse" },
|
||||||
{ name = "toml" },
|
{ name = "toml" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
|
{ name = "cx-oracle", specifier = ">=8.3.0" },
|
||||||
{ name = "pandas", specifier = ">=2.2.3" },
|
{ name = "pandas", specifier = ">=2.2.3" },
|
||||||
|
{ name = "pg8000", specifier = ">=1.31.2" },
|
||||||
{ name = "pykeepass", specifier = ">=4.1.0.post1" },
|
{ name = "pykeepass", specifier = ">=4.1.0.post1" },
|
||||||
{ name = "sqlalchemy", specifier = ">=2.0.36" },
|
{ name = "sqlalchemy", specifier = ">=2.0.36" },
|
||||||
|
{ name = "sqlparse", specifier = ">=0.5.3" },
|
||||||
{ name = "toml", specifier = ">=0.10.2" },
|
{ name = "toml", specifier = ">=0.10.2" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -164,6 +185,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 },
|
{ url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pg8000"
|
||||||
|
version = "1.31.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
{ name = "scramp" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0f/d7/0554640cbe3e193184796bedb6de23f797c03958425176faf0e694c06eb0/pg8000-1.31.2.tar.gz", hash = "sha256:1ea46cf09d8eca07fe7eaadefd7951e37bee7fabe675df164f1a572ffb300876", size = 113513 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/a0/2b30d52017c4ced8fc107386666ea7573954eb708bf66121f0229df05d41/pg8000-1.31.2-py3-none-any.whl", hash = "sha256:436c771ede71af4d4c22ba867a30add0bc5c942d7ab27fadbb6934a487ecc8f6", size = 54494 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pycparser"
|
name = "pycparser"
|
||||||
version = "2.22"
|
version = "2.22"
|
||||||
@ -229,6 +263,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 },
|
{ url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scramp"
|
||||||
|
version = "1.4.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asn1crypto" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f9/fa/8f1b99c3f875f334ac782e173ec03c35c246ec7a94fc5dd85153bc1d8285/scramp-1.4.5.tar.gz", hash = "sha256:be3fbe774ca577a7a658117dca014e5d254d158cecae3dd60332dfe33ce6d78e", size = 16169 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/9f/8b2f2749ccfbe4fcef08650896ac47ed919ff25b7ac57b7a1ae7da16c8c3/scramp-1.4.5-py3-none-any.whl", hash = "sha256:50e37c464fc67f37994e35bee4151e3d8f9320e9c204fca83a5d313c121bbbe7", size = 12781 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
@ -258,6 +304,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b8/49/21633706dd6feb14cd3f7935fc00b60870ea057686035e1a99ae6d9d9d53/SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e", size = 1883787 },
|
{ url = "https://files.pythonhosted.org/packages/b8/49/21633706dd6feb14cd3f7935fc00b60870ea057686035e1a99ae6d9d9d53/SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e", size = 1883787 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlparse"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e5/40/edede8dd6977b0d3da179a342c198ed100dd2aba4be081861ee5911e4da4/sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", size = 84999 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user