More type information
Add type information, change some imports and some style.
This commit is contained in:
parent
6bda7044ae
commit
12f4459682
@ -23,8 +23,9 @@ import urllib.parse
|
|||||||
import os.path
|
import os.path
|
||||||
import pickle
|
import pickle
|
||||||
import base64
|
import base64
|
||||||
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
from relational.relation import relation
|
from relational.relation import Relation
|
||||||
from relational import parser
|
from relational import parser
|
||||||
from relational.rtypes import is_valid_relation_name
|
from relational.rtypes import is_valid_relation_name
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ from relational.rtypes import is_valid_relation_name
|
|||||||
SWEARWORDS = {'fuck', 'shit', 'suck', 'merda', 'mierda', 'merde'}
|
SWEARWORDS = {'fuck', 'shit', 'suck', 'merda', 'mierda', 'merde'}
|
||||||
|
|
||||||
|
|
||||||
def send_survey(data):
|
def send_survey(data) -> int:
|
||||||
'''Sends the survey. Data must be a dictionary.
|
'''Sends the survey. Data must be a dictionary.
|
||||||
returns the http response.
|
returns the http response.
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ def send_survey(data):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def check_latest_version():
|
def check_latest_version() -> Optional[str]:
|
||||||
'''Returns the latest version available.
|
'''Returns the latest version available.
|
||||||
Heavely dependent on server and server configurations
|
Heavely dependent on server and server configurations
|
||||||
not granted to work forever.'''
|
not granted to work forever.'''
|
||||||
@ -78,30 +79,30 @@ def check_latest_version():
|
|||||||
return s.decode().strip()
|
return s.decode().strip()
|
||||||
|
|
||||||
|
|
||||||
class UserInterface (object):
|
class UserInterface:
|
||||||
|
|
||||||
'''It is used to provide services to the user interfaces, in order to
|
'''It is used to provide services to the user interfaces, in order to
|
||||||
reduce the amount of duplicated code present in different user interfaces.
|
reduce the amount of duplicated code present in different user interfaces.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self.session_reset()
|
self.session_reset()
|
||||||
|
|
||||||
def load(self, filename, name):
|
def load(self, filename: str, name: str) -> None:
|
||||||
'''Loads a relation from file, and gives it a name to
|
'''Loads a relation from file, and gives it a name to
|
||||||
be used in subsequent queries.'''
|
be used in subsequent queries.'''
|
||||||
rel = relation(filename)
|
rel = Relation(filename)
|
||||||
self.set_relation(name, rel)
|
self.set_relation(name, rel)
|
||||||
|
|
||||||
def unload(self, name):
|
def unload(self, name: str) -> None:
|
||||||
'''Unloads an existing relation.'''
|
'''Unloads an existing relation.'''
|
||||||
del self.relations[name]
|
del self.relations[name]
|
||||||
|
|
||||||
def store(self, filename, name):
|
def store(self, filename: str, name: str) -> None:
|
||||||
'''Stores a relation to file.'''
|
'''Stores a relation to file.'''
|
||||||
pass
|
raise Exception('Not implemented')
|
||||||
|
|
||||||
def session_dump(self, filename=None):
|
def session_dump(self, filename: Optional[str] = None) -> Optional[str]:
|
||||||
'''
|
'''
|
||||||
Dumps the session.
|
Dumps the session.
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ class UserInterface (object):
|
|||||||
inside the file, and None is returned.
|
inside the file, and None is returned.
|
||||||
|
|
||||||
If no filename is specified, the session is returned
|
If no filename is specified, the session is returned
|
||||||
as bytes.
|
as string.
|
||||||
'''
|
'''
|
||||||
if filename:
|
if filename:
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
@ -117,11 +118,11 @@ class UserInterface (object):
|
|||||||
return None
|
return None
|
||||||
return base64.b64encode(pickle.dumps(self.relations)).decode()
|
return base64.b64encode(pickle.dumps(self.relations)).decode()
|
||||||
|
|
||||||
def session_restore(self, session=None, filename=None):
|
def session_restore(self, session: Optional[bytes] = None, filename: Optional[str] = None) -> None:
|
||||||
'''
|
'''
|
||||||
Restores a session.
|
Restores a session.
|
||||||
|
|
||||||
Either from bytes or from a file
|
Either from bytes or from a file.
|
||||||
'''
|
'''
|
||||||
if session:
|
if session:
|
||||||
try:
|
try:
|
||||||
@ -132,23 +133,23 @@ class UserInterface (object):
|
|||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
self.relations = pickle.load(f)
|
self.relations = pickle.load(f)
|
||||||
|
|
||||||
def session_reset(self):
|
def session_reset(self) -> None:
|
||||||
'''
|
'''
|
||||||
Resets the session to a clean one
|
Resets the session to a clean one
|
||||||
'''
|
'''
|
||||||
self.relations = {}
|
self.relations = {}
|
||||||
|
|
||||||
def get_relation(self, name):
|
def get_relation(self, name: str) -> Relation:
|
||||||
'''Returns the relation corresponding to name.'''
|
'''Returns the relation corresponding to name.'''
|
||||||
return self.relations[name]
|
return self.relations[name]
|
||||||
|
|
||||||
def set_relation(self, name, rel):
|
def set_relation(self, name: str, rel: Relation) -> None:
|
||||||
'''Sets the relation corresponding to name.'''
|
'''Sets the relation corresponding to name.'''
|
||||||
if not is_valid_relation_name(name):
|
if not is_valid_relation_name(name):
|
||||||
raise Exception('Invalid name for destination relation')
|
raise Exception('Invalid name for destination relation')
|
||||||
self.relations[name] = rel
|
self.relations[name] = rel
|
||||||
|
|
||||||
def suggest_name(self, filename):
|
def suggest_name(self, filename: str) -> Optional[str]:
|
||||||
'''
|
'''
|
||||||
Returns a possible name for a relation, given
|
Returns a possible name for a relation, given
|
||||||
a filename.
|
a filename.
|
||||||
@ -167,7 +168,7 @@ class UserInterface (object):
|
|||||||
return None
|
return None
|
||||||
return name
|
return name
|
||||||
|
|
||||||
def execute(self, query, relname='last_'):
|
def execute(self, query: str, relname: str = 'last_') -> Relation:
|
||||||
'''Executes a query, returns the result and if
|
'''Executes a query, returns the result and if
|
||||||
relname is not None, adds the result to the
|
relname is not None, adds the result to the
|
||||||
dictionary, with the name given in relname.'''
|
dictionary, with the name given in relname.'''
|
||||||
@ -180,7 +181,7 @@ class UserInterface (object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def split_query(query, default_name='last_'):
|
def split_query(query: str, default_name='last_') -> Tuple[str, str]:
|
||||||
'''
|
'''
|
||||||
Accepts a query which might have an initial value
|
Accepts a query which might have an initial value
|
||||||
assignment
|
assignment
|
||||||
@ -196,14 +197,14 @@ class UserInterface (object):
|
|||||||
query = sq[1].strip()
|
query = sq[1].strip()
|
||||||
return default_name, query
|
return default_name, query
|
||||||
|
|
||||||
def multi_execute(self, query):
|
def multi_execute(self, query: str) -> Relation:
|
||||||
'''Executes multiple queries, separated by \n
|
'''Executes multiple queries, separated by \n
|
||||||
|
|
||||||
They can have a syntax of
|
They can have a syntax of
|
||||||
[varname =] query
|
[varname =] query
|
||||||
to assign the result to a new relation
|
to assign the result to a new relation
|
||||||
'''
|
'''
|
||||||
r = relation()
|
r = Relation()
|
||||||
queries = query.split('\n')
|
queries = query.split('\n')
|
||||||
for query in queries:
|
for query in queries:
|
||||||
if query.strip() == '':
|
if query.strip() == '':
|
||||||
|
@ -22,32 +22,23 @@
|
|||||||
# relational query, or it can be a parse tree for a relational expression (ie: class parser.node).
|
# relational query, or it can be a parse tree for a relational expression (ie: class parser.node).
|
||||||
# The functions will always return a string with the optimized query, but if a parse tree was provided,
|
# The functions will always return a string with the optimized query, but if a parse tree was provided,
|
||||||
# the parse tree itself will be modified accordingly.
|
# the parse tree itself will be modified accordingly.
|
||||||
|
from typing import Union, Optional, Dict, Any
|
||||||
|
|
||||||
from relational import optimizations
|
from relational import optimizations
|
||||||
from relational import parser
|
from relational.parser import Node, RELATION, UNARY, BINARY, op_functions, tokenize, tree
|
||||||
from relational import querysplit
|
from relational import querysplit
|
||||||
from relational.maintenance import UserInterface
|
from relational.maintenance import UserInterface
|
||||||
|
|
||||||
|
ContextDict = Dict[str,Any]
|
||||||
# Stuff that was here before, keeping it for compatibility
|
|
||||||
RELATION = parser.RELATION
|
|
||||||
UNARY = parser.UNARY
|
|
||||||
BINARY = parser.BINARY
|
|
||||||
|
|
||||||
op_functions = parser.op_functions
|
|
||||||
node = parser.node
|
|
||||||
tokenize = parser.tokenize
|
|
||||||
tree = parser.tree
|
|
||||||
# End of the stuff
|
|
||||||
|
|
||||||
|
|
||||||
def optimize_program(code, rels):
|
def optimize_program(code, rels: ContextDict):
|
||||||
'''
|
'''
|
||||||
Optimize an entire program, composed by multiple expressions
|
Optimize an entire program, composed by multiple expressions
|
||||||
and assignments.
|
and assignments.
|
||||||
'''
|
'''
|
||||||
lines = code.split('\n')
|
lines = code.split('\n')
|
||||||
context = {}
|
context = {} # type: ContextDict
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
@ -55,14 +46,14 @@ def optimize_program(code, rels):
|
|||||||
continue
|
continue
|
||||||
res, query = UserInterface.split_query(line)
|
res, query = UserInterface.split_query(line)
|
||||||
last_res = res
|
last_res = res
|
||||||
parsed = parser.tree(query)
|
parsed = tree(query)
|
||||||
optimizations.replace_leaves(parsed, context)
|
optimizations.replace_leaves(parsed, context)
|
||||||
context[res] = parsed
|
context[res] = parsed
|
||||||
node = optimize_all(context[last_res], rels, tostr=False)
|
node = optimize_all(context[last_res], rels, tostr=False)
|
||||||
return querysplit.split(node, rels)
|
return querysplit.split(node, rels)
|
||||||
|
|
||||||
|
|
||||||
def optimize_all(expression, rels, specific=True, general=True, debug=None,tostr=True):
|
def optimize_all(expression: Union[str, Node], rels: ContextDict, specific: bool = True, general: bool = True, debug: Optional[list] = None, tostr: bool = True) -> Union[str, Node]:
|
||||||
'''This function performs all the available optimizations.
|
'''This function performs all the available optimizations.
|
||||||
|
|
||||||
expression : see documentation of this module
|
expression : see documentation of this module
|
||||||
@ -76,7 +67,7 @@ def optimize_all(expression, rels, specific=True, general=True, debug=None,tostr
|
|||||||
Return value: this will return an optimized version of the expression'''
|
Return value: this will return an optimized version of the expression'''
|
||||||
if isinstance(expression, str):
|
if isinstance(expression, str):
|
||||||
n = tree(expression) # Gets the tree
|
n = tree(expression) # Gets the tree
|
||||||
elif isinstance(expression, node):
|
elif isinstance(expression, Node):
|
||||||
n = expression
|
n = expression
|
||||||
else:
|
else:
|
||||||
raise (TypeError("expression must be a string or a node"))
|
raise (TypeError("expression must be a string or a node"))
|
||||||
@ -107,7 +98,7 @@ def optimize_all(expression, rels, specific=True, general=True, debug=None,tostr
|
|||||||
return n
|
return n
|
||||||
|
|
||||||
|
|
||||||
def specific_optimize(expression, rels):
|
def specific_optimize(expression, rels: ContextDict):
|
||||||
'''This function performs specific optimizations. Means that it will need to
|
'''This function performs specific optimizations. Means that it will need to
|
||||||
know the fields used by the relations.
|
know the fields used by the relations.
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#
|
#
|
||||||
# Language definition here:
|
# Language definition here:
|
||||||
# http://ltworf.github.io/relational/grammar.html
|
# http://ltworf.github.io/relational/grammar.html
|
||||||
from typing import Optional
|
from typing import Optional, Union, List, Any
|
||||||
|
|
||||||
from relational import rtypes
|
from relational import rtypes
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ class CallableString(str):
|
|||||||
return eval(self, context)
|
return eval(self, context)
|
||||||
|
|
||||||
|
|
||||||
class Node (object):
|
class Node:
|
||||||
|
|
||||||
'''This class is a node of a relational expression. Leaves are relations
|
'''This class is a node of a relational expression. Leaves are relations
|
||||||
and internal nodes are operations.
|
and internal nodes are operations.
|
||||||
@ -105,7 +105,7 @@ class Node (object):
|
|||||||
kind = None # type: Optional[int]
|
kind = None # type: Optional[int]
|
||||||
__hash__ = None # type: None
|
__hash__ = None # type: None
|
||||||
|
|
||||||
def __init__(self, expression=None):
|
def __init__(self, expression: Optional[list] = None) -> None:
|
||||||
'''Generates the tree from the tokenized expression
|
'''Generates the tree from the tokenized expression
|
||||||
If no expression is specified then it will create an empty node'''
|
If no expression is specified then it will create an empty node'''
|
||||||
if expression == None or len(expression) == 0:
|
if expression == None or len(expression) == 0:
|
||||||
@ -172,7 +172,7 @@ class Node (object):
|
|||||||
code = self._toPython()
|
code = self._toPython()
|
||||||
return compile(code, '<relational_expression>', 'eval')
|
return compile(code, '<relational_expression>', 'eval')
|
||||||
|
|
||||||
def toPython(self):
|
def toPython(self) -> CallableString:
|
||||||
'''This method converts the AST into a python code string, which
|
'''This method converts the AST into a python code string, which
|
||||||
will require the relation module to be executed.
|
will require the relation module to be executed.
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ class Node (object):
|
|||||||
directly called.'''
|
directly called.'''
|
||||||
return CallableString(self._toPython())
|
return CallableString(self._toPython())
|
||||||
|
|
||||||
def _toPython(self):
|
def _toPython(self) -> str:
|
||||||
'''
|
'''
|
||||||
Same as toPython but returns a regular string
|
Same as toPython but returns a regular string
|
||||||
'''
|
'''
|
||||||
@ -201,7 +201,7 @@ class Node (object):
|
|||||||
return '%s.%s(%s)' % (self.child.toPython(), op_functions[self.name], prop)
|
return '%s.%s(%s)' % (self.child.toPython(), op_functions[self.name], prop)
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def printtree(self, level=0):
|
def printtree(self, level: int = 0) -> str:
|
||||||
'''returns a representation of the tree using indentation'''
|
'''returns a representation of the tree using indentation'''
|
||||||
r = ''
|
r = ''
|
||||||
for i in range(level):
|
for i in range(level):
|
||||||
@ -213,10 +213,9 @@ class Node (object):
|
|||||||
elif self.name in u_operators:
|
elif self.name in u_operators:
|
||||||
r += '\t%s\n' % self.prop
|
r += '\t%s\n' % self.prop
|
||||||
r += self.child.printtree(level + 1)
|
r += self.child.printtree(level + 1)
|
||||||
|
|
||||||
return '\n' + r
|
return '\n' + r
|
||||||
|
|
||||||
def get_left_leaf(self):
|
def get_left_leaf(self) -> 'Node':
|
||||||
'''This function returns the leftmost leaf in the tree.'''
|
'''This function returns the leftmost leaf in the tree.'''
|
||||||
if self.kind == RELATION:
|
if self.kind == RELATION:
|
||||||
return self
|
return self
|
||||||
@ -225,12 +224,12 @@ class Node (object):
|
|||||||
elif self.kind == BINARY:
|
elif self.kind == BINARY:
|
||||||
return self.left.get_left_leaf()
|
return self.left.get_left_leaf()
|
||||||
|
|
||||||
def result_format(self, rels):
|
def result_format(self, rels: dict) -> list:
|
||||||
'''This function returns a list containing the fields that the resulting relation will have.
|
'''This function returns a list containing the fields that the resulting relation will have.
|
||||||
It requires a dictionary where keys are the names of the relations and the values are
|
It requires a dictionary where keys are the names of the relations and the values are
|
||||||
the relation objects.'''
|
the relation objects.'''
|
||||||
if rels == None:
|
if not isinstance(rels, dict):
|
||||||
return
|
raise TypeError('Can\'t be of None type')
|
||||||
|
|
||||||
if self.kind == RELATION:
|
if self.kind == RELATION:
|
||||||
return list(rels[self.name].header)
|
return list(rels[self.name].header)
|
||||||
@ -285,7 +284,7 @@ class Node (object):
|
|||||||
return (le + self.name + re)
|
return (le + self.name + re)
|
||||||
|
|
||||||
|
|
||||||
def _find_matching_parenthesis(expression, start=0, openpar=u'(', closepar=u')'):
|
def _find_matching_parenthesis(expression: str, start=0, openpar=u'(', closepar=u')') -> int:
|
||||||
'''This function returns the position of the matching
|
'''This function returns the position of the matching
|
||||||
close parenthesis to the 1st open parenthesis found
|
close parenthesis to the 1st open parenthesis found
|
||||||
starting from start (0 by default)'''
|
starting from start (0 by default)'''
|
||||||
@ -304,7 +303,6 @@ def _find_matching_parenthesis(expression, start=0, openpar=u'(', closepar=u')')
|
|||||||
if string:
|
if string:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
if expression[i] == openpar:
|
if expression[i] == openpar:
|
||||||
par_count += 1
|
par_count += 1
|
||||||
elif expression[i] == closepar:
|
elif expression[i] == closepar:
|
||||||
@ -312,7 +310,7 @@ def _find_matching_parenthesis(expression, start=0, openpar=u'(', closepar=u')')
|
|||||||
if par_count == 0:
|
if par_count == 0:
|
||||||
return i # Closing parenthesis of the parameter
|
return i # Closing parenthesis of the parameter
|
||||||
|
|
||||||
def _find_token(haystack, needle):
|
def _find_token(haystack: str, needle: str) -> int:
|
||||||
'''
|
'''
|
||||||
Like the string function find, but
|
Like the string function find, but
|
||||||
ignores tokens that are within a string
|
ignores tokens that are within a string
|
||||||
@ -337,17 +335,17 @@ def _find_token(haystack, needle):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def tokenize(expression):
|
def tokenize(expression: str) -> list:
|
||||||
'''This function converts a relational expression into a list where
|
'''This function converts a relational expression into a list where
|
||||||
every token of the expression is an item of a list. Expressions into
|
every token of the expression is an item of a list. Expressions into
|
||||||
parenthesis will be converted into sublists.'''
|
parenthesis will be converted into sublists.'''
|
||||||
|
|
||||||
items = [] # List for the tokens
|
# List for the tokens
|
||||||
|
items = [] # type: List[Union[str,list]]
|
||||||
|
|
||||||
expression = expression.strip() # Removes initial and ending spaces
|
expression = expression.strip() # Removes initial and ending spaces
|
||||||
|
|
||||||
while len(expression) > 0:
|
while len(expression) > 0:
|
||||||
|
|
||||||
if expression.startswith('('): # Parenthesis state
|
if expression.startswith('('): # Parenthesis state
|
||||||
end = _find_matching_parenthesis(expression)
|
end = _find_matching_parenthesis(expression)
|
||||||
if end == None:
|
if end == None:
|
||||||
@ -384,17 +382,16 @@ def tokenize(expression):
|
|||||||
break
|
break
|
||||||
items.append(expression[:r])
|
items.append(expression[:r])
|
||||||
expression = expression[r:].strip()
|
expression = expression[r:].strip()
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def tree(expression):
|
def tree(expression: str) -> Node:
|
||||||
'''This function parses a relational algebra expression into a AST and returns
|
'''This function parses a relational algebra expression into a AST and returns
|
||||||
the root node using the Node class.'''
|
the root node using the Node class.'''
|
||||||
return node(tokenize(expression))
|
return Node(tokenize(expression))
|
||||||
|
|
||||||
|
|
||||||
def parse(expr):
|
def parse(expr: str) -> CallableString:
|
||||||
'''This function parses a relational algebra expression, and returns a
|
'''This function parses a relational algebra expression, and returns a
|
||||||
CallableString (a string that can be called) whith the corresponding
|
CallableString (a string that can be called) whith the corresponding
|
||||||
Python expression.
|
Python expression.
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
import csv
|
import csv
|
||||||
from itertools import chain, repeat
|
from itertools import chain, repeat
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
from relational.rtypes import *
|
from relational.rtypes import *
|
||||||
|
|
||||||
@ -52,9 +53,9 @@ class Relation (object):
|
|||||||
'''
|
'''
|
||||||
__hash__ = None # type: None
|
__hash__ = None # type: None
|
||||||
|
|
||||||
def __init__(self, filename=""):
|
def __init__(self, filename : str = '') -> None:
|
||||||
self._readonly = False
|
self._readonly = False
|
||||||
self.content = set()
|
self.content = set() # type: Set[tuple]
|
||||||
|
|
||||||
if len(filename) == 0: # Empty relation
|
if len(filename) == 0: # Empty relation
|
||||||
self.header = Header([])
|
self.header = Header([])
|
||||||
@ -65,14 +66,14 @@ class Relation (object):
|
|||||||
iterator = ((self.insert(i) for i in reader))
|
iterator = ((self.insert(i) for i in reader))
|
||||||
deque(iterator, maxlen=0)
|
deque(iterator, maxlen=0)
|
||||||
|
|
||||||
def _make_duplicate(self, copy):
|
def _make_duplicate(self, copy: 'Relation') -> None:
|
||||||
'''Flag that the relation "copy" is pointing
|
'''Flag that the relation "copy" is pointing
|
||||||
to the same set as this relation.'''
|
to the same set as this relation.'''
|
||||||
|
|
||||||
self._readonly = True
|
self._readonly = True
|
||||||
copy._readonly = True
|
copy._readonly = True
|
||||||
|
|
||||||
def _make_writable(self, copy_content=True):
|
def _make_writable(self, copy_content : bool = True) -> None:
|
||||||
'''If this relation is marked as readonly, this
|
'''If this relation is marked as readonly, this
|
||||||
method will copy the content to make it writable too
|
method will copy the content to make it writable too
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ class Relation (object):
|
|||||||
def __contains__(self, key):
|
def __contains__(self, key):
|
||||||
return key in self.content
|
return key in self.content
|
||||||
|
|
||||||
def save(self, filename):
|
def save(self, filename: str) -> None:
|
||||||
'''
|
'''
|
||||||
Saves the relation in a file. Will save using the csv
|
Saves the relation in a file. Will save using the csv
|
||||||
format as defined in RFC4180.
|
format as defined in RFC4180.
|
||||||
@ -107,7 +108,7 @@ class Relation (object):
|
|||||||
# Writing content, already in the correct format
|
# Writing content, already in the correct format
|
||||||
writer.writerows(self.content)
|
writer.writerows(self.content)
|
||||||
|
|
||||||
def _rearrange(self, other):
|
def _rearrange(self, other: 'Relation') -> 'Relation':
|
||||||
'''If two relations share the same attributes in a different order, this method
|
'''If two relations share the same attributes in a different order, this method
|
||||||
will use projection to make them have the same attributes' order.
|
will use projection to make them have the same attributes' order.
|
||||||
It is not exactely related to relational algebra. Just a method used
|
It is not exactely related to relational algebra. Just a method used
|
||||||
@ -123,7 +124,7 @@ class Relation (object):
|
|||||||
','.join(self.header), ','.join(other.header)
|
','.join(self.header), ','.join(other.header)
|
||||||
))
|
))
|
||||||
|
|
||||||
def selection(self, expr):
|
def selection(self, expr: str) -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Selection, expr must be a valid Python expression; can contain field names.
|
Selection, expr must be a valid Python expression; can contain field names.
|
||||||
'''
|
'''
|
||||||
@ -149,7 +150,7 @@ class Relation (object):
|
|||||||
"Failed to evaluate %s\n%s" % (expr, e.__str__()))
|
"Failed to evaluate %s\n%s" % (expr, e.__str__()))
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def product(self, other):
|
def product(self, other: 'Relation') -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Cartesian product. Attributes of the relations must differ.
|
Cartesian product. Attributes of the relations must differ.
|
||||||
'''
|
'''
|
||||||
@ -168,7 +169,7 @@ class Relation (object):
|
|||||||
newt.content.add(i + j)
|
newt.content.add(i + j)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def projection(self, * attributes):
|
def projection(self, * attributes) -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Can be called in two different ways:
|
Can be called in two different ways:
|
||||||
a.projection('field1','field2')
|
a.projection('field1','field2')
|
||||||
@ -199,7 +200,7 @@ class Relation (object):
|
|||||||
newt.content.add(tuple(row))
|
newt.content.add(tuple(row))
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def rename(self, params):
|
def rename(self, params: 'Relation') -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Takes a dictionary.
|
Takes a dictionary.
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ class Relation (object):
|
|||||||
self._make_duplicate(newt)
|
self._make_duplicate(newt)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def intersection(self, other):
|
def intersection(self, other: 'Relation') -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Intersection operation. The result will contain items present in both
|
Intersection operation. The result will contain items present in both
|
||||||
operands.
|
operands.
|
||||||
@ -228,7 +229,7 @@ class Relation (object):
|
|||||||
newt.content = self.content.intersection(other.content)
|
newt.content = self.content.intersection(other.content)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def difference(self, other):
|
def difference(self, other: 'Relation') -> 'Relation':
|
||||||
'''Difference operation. The result will contain items present in first
|
'''Difference operation. The result will contain items present in first
|
||||||
operand but not in second one.
|
operand but not in second one.
|
||||||
'''
|
'''
|
||||||
@ -239,7 +240,7 @@ class Relation (object):
|
|||||||
newt.content = self.content.difference(other.content)
|
newt.content = self.content.difference(other.content)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def division(self, other):
|
def division(self, other: 'Relation') -> 'Relation':
|
||||||
'''Division operator
|
'''Division operator
|
||||||
The division is a binary operation that is written as R ÷ S. The
|
The division is a binary operation that is written as R ÷ S. The
|
||||||
result consists of the restrictions of tuples in R to the
|
result consists of the restrictions of tuples in R to the
|
||||||
@ -265,7 +266,7 @@ class Relation (object):
|
|||||||
t = self.projection(d_headers).product(other)
|
t = self.projection(d_headers).product(other)
|
||||||
return self.projection(d_headers).difference(t.difference(self).projection(d_headers))
|
return self.projection(d_headers).difference(t.difference(self).projection(d_headers))
|
||||||
|
|
||||||
def union(self, other):
|
def union(self, other: 'Relation') -> 'Relation':
|
||||||
'''Union operation. The result will contain items present in first
|
'''Union operation. The result will contain items present in first
|
||||||
and second operands.
|
and second operands.
|
||||||
'''
|
'''
|
||||||
@ -276,18 +277,18 @@ class Relation (object):
|
|||||||
newt.content = self.content.union(other.content)
|
newt.content = self.content.union(other.content)
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def thetajoin(self, other, expr):
|
def thetajoin(self, other: 'Relation', expr: str) -> 'Relation':
|
||||||
'''Defined as product and then selection with the given expression.'''
|
'''Defined as product and then selection with the given expression.'''
|
||||||
return self.product(other).selection(expr)
|
return self.product(other).selection(expr)
|
||||||
|
|
||||||
def outer(self, other):
|
def outer(self, other: 'Relation') -> 'Relation':
|
||||||
'''Does a left and a right outer join and returns their union.'''
|
'''Does a left and a right outer join and returns their union.'''
|
||||||
a = self.outer_right(other)
|
a = self.outer_right(other)
|
||||||
b = self.outer_left(other)
|
b = self.outer_left(other)
|
||||||
|
|
||||||
return a.union(b)
|
return a.union(b)
|
||||||
|
|
||||||
def outer_right(self, other):
|
def outer_right(self, other: 'Relation') -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Outer right join. Considers self as left and param as right. If the
|
Outer right join. Considers self as left and param as right. If the
|
||||||
tuple has no corrispondence, empy attributes are filled with a "---"
|
tuple has no corrispondence, empy attributes are filled with a "---"
|
||||||
@ -297,7 +298,7 @@ class Relation (object):
|
|||||||
'''
|
'''
|
||||||
return other.outer_left(self)
|
return other.outer_left(self)
|
||||||
|
|
||||||
def outer_left(self, other, swap=False):
|
def outer_left(self, other: 'Relation', swap=False) -> 'Relation':
|
||||||
'''
|
'''
|
||||||
See documentation for outer_right
|
See documentation for outer_right
|
||||||
'''
|
'''
|
||||||
@ -338,7 +339,7 @@ class Relation (object):
|
|||||||
|
|
||||||
return newt
|
return newt
|
||||||
|
|
||||||
def join(self, other):
|
def join(self, other: 'Relation') -> 'Relation':
|
||||||
'''
|
'''
|
||||||
Natural join, joins on shared attributes (one or more). If there are no
|
Natural join, joins on shared attributes (one or more). If there are no
|
||||||
shared attributes, it will behave as the cartesian product.
|
shared attributes, it will behave as the cartesian product.
|
||||||
@ -412,7 +413,7 @@ class Relation (object):
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def update(self, expr, dic):
|
def update(self, expr: str, dic: dict) -> int:
|
||||||
'''
|
'''
|
||||||
Updates certain values of a relation.
|
Updates certain values of a relation.
|
||||||
|
|
||||||
@ -444,7 +445,7 @@ class Relation (object):
|
|||||||
self.content = not_affected.content
|
self.content = not_affected.content
|
||||||
return len(affected)
|
return len(affected)
|
||||||
|
|
||||||
def insert(self, values):
|
def insert(self, values: Union[list,tuple]) -> int:
|
||||||
'''
|
'''
|
||||||
Inserts a tuple in the relation.
|
Inserts a tuple in the relation.
|
||||||
This function will not insert duplicate tuples.
|
This function will not insert duplicate tuples.
|
||||||
@ -468,7 +469,7 @@ class Relation (object):
|
|||||||
self.content.add(tuple(map(rstring, values)))
|
self.content.add(tuple(map(rstring, values)))
|
||||||
return len(self.content) - prevlen
|
return len(self.content) - prevlen
|
||||||
|
|
||||||
def delete(self, expr):
|
def delete(self, expr: str) -> int:
|
||||||
'''
|
'''
|
||||||
Delete, expr must be a valid Python expression; can contain field names.
|
Delete, expr must be a valid Python expression; can contain field names.
|
||||||
|
|
||||||
@ -504,7 +505,7 @@ class Header(tuple):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "Header(%s)" % super(Header, self).__repr__()
|
return "Header(%s)" % super(Header, self).__repr__()
|
||||||
|
|
||||||
def rename(self, params):
|
def rename(self, params) -> 'Header':
|
||||||
'''Returns a new header, with renamed fields.
|
'''Returns a new header, with renamed fields.
|
||||||
|
|
||||||
params is a dictionary of {old:new} names
|
params is a dictionary of {old:new} names
|
||||||
@ -520,19 +521,19 @@ class Header(tuple):
|
|||||||
raise Exception('Field not found: %s' % old)
|
raise Exception('Field not found: %s' % old)
|
||||||
return Header(attrs)
|
return Header(attrs)
|
||||||
|
|
||||||
def sharedAttributes(self, other):
|
def sharedAttributes(self, other: 'Header') -> int:
|
||||||
'''Returns how many attributes this header has in common with a given one'''
|
'''Returns how many attributes this header has in common with a given one'''
|
||||||
return len(set(self).intersection(set(other)))
|
return len(set(self).intersection(set(other)))
|
||||||
|
|
||||||
def union(self, other):
|
def union(self, other) -> set:
|
||||||
'''Returns the union of the sets of attributes with another header.'''
|
'''Returns the union of the sets of attributes with another header.'''
|
||||||
return set(self).union(set(other))
|
return set(self).union(set(other))
|
||||||
|
|
||||||
def intersection(self, other):
|
def intersection(self, other) -> set:
|
||||||
'''Returns the set of common attributes with another header.'''
|
'''Returns the set of common attributes with another header.'''
|
||||||
return set(self).intersection(set(other))
|
return set(self).intersection(set(other))
|
||||||
|
|
||||||
def getAttributesId(self, param):
|
def getAttributesId(self, param) -> List[int]:
|
||||||
'''Returns a list with numeric index corresponding to field's name'''
|
'''Returns a list with numeric index corresponding to field's name'''
|
||||||
try:
|
try:
|
||||||
return [self.index(i) for i in param]
|
return [self.index(i) for i in param]
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import keyword
|
import keyword
|
||||||
import re
|
import re
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
RELATION_NAME_REGEXP = re.compile(r'^[_a-z][_a-z0-9]*$', re.IGNORECASE)
|
RELATION_NAME_REGEXP = re.compile(r'^[_a-z][_a-z0-9]*$', re.IGNORECASE)
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ class Rstring (str):
|
|||||||
r'^([0-9]{1,4})(\\|-|/)([0-9]{1,2})(\\|-|/)([0-9]{1,2})$'
|
r'^([0-9]{1,4})(\\|-|/)([0-9]{1,2})(\\|-|/)([0-9]{1,2})$'
|
||||||
)
|
)
|
||||||
|
|
||||||
def autocast(self):
|
def autocast(self) -> Union[int, float, 'Rdate', 'Rstring']:
|
||||||
'''
|
'''
|
||||||
Returns the automatic cast for this
|
Returns the automatic cast for this
|
||||||
value.
|
value.
|
||||||
@ -47,7 +48,7 @@ class Rstring (str):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self._autocast = self
|
self._autocast = self # type: Union[int, float, 'Rdate', 'Rstring']
|
||||||
if len(self) > 0:
|
if len(self) > 0:
|
||||||
if self.isInt():
|
if self.isInt():
|
||||||
self._autocast = int(self)
|
self._autocast = int(self)
|
||||||
@ -80,7 +81,7 @@ class Rstring (str):
|
|||||||
no more parsings are needed
|
no more parsings are needed
|
||||||
'''
|
'''
|
||||||
try:
|
try:
|
||||||
return self._isdate
|
return self._isdate # type: ignore
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user