From 163d4984ecf018658539b5397b2c4a25e1569899 Mon Sep 17 00:00:00 2001 From: V1A0 <54343363+V1A0@users.noreply.github.com> Date: Sun, 30 May 2021 15:05:56 +0700 Subject: [PATCH] v0.1.9.0 - Issue #17 fixed, SELECT-like methods now **allways** returns List[List[...]] objects - Tests remastering - Code refactoring - Readme upd --- README.md | 5 +++ setup.py | 4 +- sqllex/classes/sqlite3x.py | 54 +++++++++++++++--------- sqllex/constants/sql.py | 70 +++++++++++++++++-------------- tests/new_test_all.py | 84 +++++++++++++++++--------------------- tests/old_test_all_1.py | 2 +- tests/temp_test_1.py | 26 +++++++++--- 7 files changed, 142 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index ebade60..8fa1513 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,11 @@ pip install sqllex If you need most stable version install **sqllex==0.1.8.12** +| version | status | +| :--------: | :---------------------------- | +| `0.1.8.12` | ✔️ (most) stable | +| `0.1.9.0` | ⚠️ unstable (experimental) | + ## About Use databases without thinking about SQL. Let me show you how sqllex makes diff --git a/setup.py b/setup.py index ccafb81..bc47faa 100644 --- a/setup.py +++ b/setup.py @@ -10,12 +10,12 @@ packages=[ 'sqllex', 'sqllex.classes', 'sqllex.constants', 'sqllex.exceptions', 'sqllex.types', 'sqllex.debug', ], - version='0.1.8.12', + version='0.1.9.0', license='gpl-3.0', description='Better than sqlite3', author='v1a0', url='https://github.com/v1a0/sqllex', - download_url='https://github.com/V1A0/sqllex/archive/refs/tags/v0.1.8.12.tar.gz', + download_url='https://github.com/V1A0/sqllex/archive/refs/tags/v0.1.9.0.tar.gz', keywords=['sql', 'sql3', 'sqlite', 'sqlite3', 'sqllex', 'db', 'database', 'easy'], install_requires=[ 'colorama==0.4.4', diff --git a/sqllex/classes/sqlite3x.py b/sqllex/classes/sqlite3x.py index 69792ac..ad0a9a2 100644 --- a/sqllex/classes/sqlite3x.py +++ b/sqllex/classes/sqlite3x.py @@ -14,7 +14,7 @@ def col_types_sort(val: Union[DataType, AnyStr]) -> int: :return: index of priority, if unknown returns 1 """ - prior = CONST_PRIORITY.get(val) + prior = CONST_PRIORITY.get(val) # How about set dict.setdefault(1) ? if prior is None: return 1 @@ -60,7 +60,7 @@ def with_wrapper(*args, **kwargs): with_dict: None = None if with_dict: - script = f"WITH " + script = f"WITH RECURSIVE " values = [] for (var, statement) in with_dict.items(): @@ -112,6 +112,7 @@ def __where__(func: callable) -> callable: def where_wrapper(*args, **kwargs): if "WHERE" in kwargs.keys(): where_: WhereType = kwargs.pop("WHERE") + else: where_: None = None @@ -134,6 +135,7 @@ def where_wrapper(*args, **kwargs): for wh in where_: # List[List] -> Dict[val[0], val[1]] + if isinstance(wh[0], str) and len(wh) > 1: new_where.update({wh[0]: wh[1:]}) else: @@ -143,6 +145,7 @@ def where_wrapper(*args, **kwargs): if isinstance(where_, dict): for (key, values) in where_.items(): + if not isinstance(values, list): values = [values] @@ -159,6 +162,7 @@ def where_wrapper(*args, **kwargs): "<>", ]: operator = values.pop(0) + if len(values) == 1 and isinstance( values[0], list ): @@ -531,12 +535,13 @@ def wrap(self, *args, **kwargs): return wrap -def lister(value: Any): +def lister(value: Any, remove_one_len: bool = False): """ Function converting input value from Tuple[Any] or List[Tuple] with any deepness to List[List] :param value: Any value contains tuples + :param remove_one_len: Convert :return: Decorated method with update after it was run """ @@ -544,12 +549,18 @@ def lister(value: Any): value = list(value) if isinstance(value, list): - if len(value) == 1: - return lister(value[0]) + if remove_one_len and (len(value) == 1): + return lister( + value[0], + remove_one_len + ) for r in range(len(value)): if isinstance(value[r], (list, tuple)): - value[r] = lister(value[r]) + value[r] = lister( + value[r], + remove_one_len + ) return value @@ -563,9 +574,11 @@ def tuples_to_lists(func: callable) -> callable: """ def t2l_wrapper(*args, **kwargs): - ret = lister(func(*args, **kwargs)) + ret = func(*args, **kwargs) if not issubclass(ret.__class__, SQLStatement): + ret = lister(ret) + if not isinstance(ret, list): ret = [ret] @@ -964,7 +977,6 @@ def _get_tables_(self) -> Generator[SQLite3xTable, None, None]: # make it never end, because in the end of generation it'll be overridden self.tables = self._get_tables_() - @tuples_to_lists def _get_tables_names_(self) -> List[str]: """ Get list of tables names @@ -972,7 +984,10 @@ def _get_tables_names_(self) -> List[str]: :return: None """ - return self.execute("SELECT name FROM sqlite_master WHERE type='table'") + return lister( + self.execute("SELECT name FROM sqlite_master WHERE type='table'"), + remove_one_len=True + ) @tuples_to_lists @__execute__ @@ -1248,7 +1263,6 @@ def _select_stmt_( """ Parent method for all SELECT-like methods """ - if not TABLE: raise ArgumentError(TABLE="Argument unset and have not default value") @@ -1345,7 +1359,7 @@ def connect(self): """ Create connection to database - :return: sqlite3.connection + :return: None """ if not self.connection: @@ -1571,7 +1585,7 @@ def markup( def get_columns( self, table: AnyStr - ) -> Union[Tuple, List]: + ) -> List[str]: """ Get list of table columns @@ -1579,16 +1593,16 @@ def get_columns( :return: List[List] of columns """ - columns = self.execute(f"SELECT name FROM PRAGMA_TABLE_INFO('{table}')") + columns_: List[List[str]] = self.execute(f"SELECT name FROM PRAGMA_TABLE_INFO('{table}')") - if not isinstance(columns, list): - columns = [columns] - - if columns: - return columns - else: + if not columns_: raise TableInfoError + # if not isinstance(columns, list): + # columns = [columns] + + return list(map(lambda columns: columns[0], columns_)) + def insert( self, TABLE: AnyStr, @@ -1744,6 +1758,8 @@ def select( WHERE = kwargs kwargs = {} + + return self._select_stmt_( SELECT=SELECT, TABLE=TABLE, diff --git a/sqllex/constants/sql.py b/sqllex/constants/sql.py index a8a85c3..08c7fc5 100644 --- a/sqllex/constants/sql.py +++ b/sqllex/constants/sql.py @@ -97,34 +97,44 @@ __all__ = [ - "ALL", - "TEXT", - "NUMERIC", - "INTEGER", - "REAL", - "NONE", - "BLOB", - "NOT_NULL", - "DEFAULT", - "UNIQUE", - "AS", - "ON", - - "PRIMARY_KEY", - "CHECK", - "AUTOINCREMENT", - "FOREIGN_KEY", - "REFERENCES", - "ABORT", - "FAIL", - "IGNORE", - "REPLACE", - "ROLLBACK", - "NULL", - - "CONST_PRIORITY", - - "INNER_JOIN", - "LEFT_JOIN", - "CROSS_JOIN", + 'ABORT', + 'ALL', + 'AS', + 'AUTOINCREMENT', + + 'BLOB', + + 'CHECK', + 'CONST_PRIORITY', + 'CROSS_JOIN', + + 'DEFAULT', + + 'FAIL', + 'FOREIGN_KEY', + + 'IGNORE', + 'INNER_JOIN', + 'INTEGER', + + 'LEFT_JOIN', + + 'NONE', + 'NOT_NULL', + 'NULL', + 'NUMERIC', + + 'ON', + + 'PRIMARY_KEY', + + 'REAL', + 'REFERENCES', + 'REPLACE', + 'ROLLBACK', + + 'TEXT', + + 'UNIQUE' ] + diff --git a/tests/new_test_all.py b/tests/new_test_all.py index 62eb262..74c0f30 100644 --- a/tests/new_test_all.py +++ b/tests/new_test_all.py @@ -28,7 +28,6 @@ def remove_db(): def tables_test(): - db = SQLite3x(path=DB_NAME, template=DB_TEMPLATE) db.markup( @@ -73,7 +72,6 @@ def tables_test(): def insert_test(): - # just arg values db.insert("t1", 'asdf', 10.0, 1, 3.14, None, 2) db["t1"].insert('asdf', 10.0, 1, 3.14, None, 2) @@ -112,8 +110,7 @@ def insert_test(): def select_test(): - - if not db.select('t1', 'text_t') == ['asdf'] * 10: + if not db.select('t1', 'text_t') == [['asdf']] * 10: print(db.select('t1', 'text_t')) raise FileExistsError @@ -130,7 +127,7 @@ def select_test(): raise FileExistsError # WHERE as dict - if not db.select('t1', 'text_t', 'num_t', WHERE={'num_t': ['=', 11.1], 'blob_t': ['<=', 5]}) == ['qwerty1', 11.1]: + if not db.select('t1', 'text_t', 'num_t', WHERE={'num_t': ['=', 11.1], 'blob_t': ['<=', 5]}) == [['qwerty1', 11.1]]: print(db.select('t1', ['text_t', 'num_t'], WHERE={'num_t': 11.1, 'blob_t': 5})) raise FileExistsError @@ -140,7 +137,7 @@ def select_test(): raise FileExistsError # WHERE as kwargs - if not db.select('t1', 'text_t', 'num_t', num_t=11.1, blob_t=6) == ['qwerty2', 11.1]: + if not db.select('t1', 'text_t', 'num_t', num_t=11.1, blob_t=6) == [['qwerty2', 11.1]]: print(db.select('t1', 'text_t', 'num_t', num_t=11.1, blob_t=6)) raise FileExistsError @@ -165,59 +162,57 @@ def select_test(): db.insertmany('t2', [[1], [2], [3], [4]]) # ORDER_BY ASC - if not db.select('t2', 'id', ORDER_BY='id ASC') == [1, 2, 3, 4]: + if not db.select('t2', 'id', ORDER_BY='id ASC') == [[1], [2], [3], [4]]: print(db.select('t2', 'id', ORDER_BY='id ASC')) raise FileExistsError # ORDER_BY DESC - if not db.select('t2', 'id', ORDER_BY='id DESC') == [4, 3, 2, 1]: + if not db.select('t2', 'id', ORDER_BY='id DESC') == [[4], [3], [2], [1]]: print(db.select('t2', 'id', ORDER_BY='id DESC')) raise FileExistsError # WITH & WHERE if not db.select( - 't2', - WHERE={ - 'id': ['<', 'xxx'] - }, - WITH={ - 'xxx': db.select('t2', 'MAX(id)', execute=False) - } - ) == [[1, 8], [2, 8], [3, 8], [4, 8]]: - print(db.select('t2',WHERE={'id': ['<', 'xxx']}, WITH={'xxx': db.select('t2', 'MAX(id)', execute=False)})) + 'xxx', + WITH={ + 'xxx': db.select('t2', 'MAX(id)', execute=False) + } + ) == [[4]]: + print(db.select( + 'xxx', + WITH={ + 'xxx': db.select('t2', 'MAX(id)', execute=False) + } + )) raise FileExistsError if not db.select( - 't2', - WHERE={ - 'id': ['<', 'xxx'] - }, - WITH={ - 'xxx': 'SELECT MAX(id) FROM t2' - } - ) == [[1, 8], [2, 8], [3, 8], [4, 8]]: - print(db.select('t2', WHERE={'id': ['<', 'xxx']}, WITH={'xxx': 'SELECT MAX(id) FROM t2'})) + 't2', + WHERE={ + 'id': ['<', 3] + }, + ) == [[1, 8], [2, 8]]: + print(db.select('t2', WHERE={'id': ['<', 3]})) raise FileExistsError # JOIN if not db.select( - 't2', - 'id', - JOIN=[ - [CROSS_JOIN, 't1', AS, 't', ON, 't.num_t > t2.value'] - ] + 't2', + 'id', + JOIN=[ + [CROSS_JOIN, 't1', AS, 't', ON, 't.num_t > t2.value'] + ] ) == [ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 - ]: + [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], + [2], [2], [2], [2], [2], [2], [2], [2], [2], [2], [2], [2], + [3], [3], [3], [3], [3], [3], [3], [3], [3], [3], [3], [3], + [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4], [4] + ]: print(db.select('t2', 'id', JOIN=[[CROSS_JOIN, 't1', AS, 't', ON, 't.num_t > t2.value']])) raise FileExistsError def insertmany_test(): - db.create_table( 't3', { @@ -226,11 +221,11 @@ def insertmany_test(): } ) - db.insertmany('t3', [[1, 'hi']]*100) + db.insertmany('t3', [[1, 'hi']] * 100) db.insertmany('t3', [[1]] * 100) - db.insertmany('t3', ((1, 'hi'),)*100) + db.insertmany('t3', ((1, 'hi'),) * 100) db.insertmany('t3', ((1,),) * 100) - db.insertmany('t3', id=[2]*10) + db.insertmany('t3', id=[2] * 10) db.insertmany('t3', id=(2,) * 10) @@ -253,7 +248,7 @@ def update_test(): } ) - if not db.select('t4', 'id', WHERE={"val": 'NEW_VAL'}) == [x for x in range(50)]: + if not db.select('t4', 'id', WHERE={"val": 'NEW_VAL'}) == [[x] for x in range(50)]: print(db.select('t4', 'id', WHERE={"val": 'NEW_VAL'})) raise FileExistsError @@ -279,7 +274,7 @@ def replace_test(): db.replace('t5', [99, 'O_O']) - if not db.select('t5', val='O_O') == [99, 'O_O']: + if not db.select('t5', val='O_O') == [[99, 'O_O']]: print(db.select('t5', val='O_O')) raise FileExistsError @@ -321,12 +316,11 @@ def get_tables_test(): replace_test() get_tables_test() - # Disconnect db.disconnect() # Time counting -t = time()-t +t = time() - t # Little sleep and printing sleep(0.1) @@ -334,5 +328,3 @@ def get_tables_test(): # Remove db remove_db() - - diff --git a/tests/old_test_all_1.py b/tests/old_test_all_1.py index 81aa410..5cd0c27 100644 --- a/tests/old_test_all_1.py +++ b/tests/old_test_all_1.py @@ -193,7 +193,7 @@ def is_exist(file: str = ''): # print(*db.tables) -print(db.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='about'")[0].split('\n')) +print(db.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='about'")[0][0].split('\n')) db.disconnect() diff --git a/tests/temp_test_1.py b/tests/temp_test_1.py index f3a69db..6f306b9 100644 --- a/tests/temp_test_1.py +++ b/tests/temp_test_1.py @@ -1,14 +1,30 @@ -# import all from sqllex package from sqllex import * -# Create database file 'database.db' db = SQLite3x(path='database.db') -# Create table inside database -db.create_table( - 'users', +db.markup({ + 'users': + { + 'id': [INTEGER, UNIQUE], + 'username': TEXT + }, + 'users2': { 'id': [INTEGER, UNIQUE], 'username': TEXT } +} ) + +users_table = db['users'] # get table as object +ut2 = db['users2'] # get table as object + +ut2.insert(1, 'A') +ut2.insert(2, 'B') + +print(users_table.columns) # ['id', 'username'] + +users_table.insert(1, "New_user") # insert new record in table + +print(users_table.select()) +print(ut2.select())