Skip to content

Instantly share code, notes, and snippets.

@pshirali
Last active May 23, 2021 16:50
Show Gist options
  • Save pshirali/b7f9ec7276303a1cf98ac6a7b5e95a56 to your computer and use it in GitHub Desktop.
Save pshirali/b7f9ec7276303a1cf98ac6a7b5e95a56 to your computer and use it in GitHub Desktop.
data_access.py
#!/usr/bin/env python3
# Example for middleware constructs to handle query and data exchange
# https://rtindru.medium.com/python-middleware-api-client-design-patterns-7c4261ecff88
class Store:
'''Base class. Stores metadata with which it was instantiated.
This class could be extended to perform validation, serialization etc'''
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
def __repr__(self):
ra = ",".join([str(a) for a in self.args])
rk = ",".join(["{}={}".format(k, self.kwargs[k]) for k in self.kwargs])
return "{}({})".format(self.__class__.__name__, ra + rk)
class ORClass:
'''OR is a singleton to indicate an infix OR operation. This class exists
solely to define a repr'''
def __repr__(self):
return "OR"
OR = ORClass() # singleton 'OR' to be used
def build(class_list, parent_class):
'''Helper to quickly build classes with different identities for this
demo. Populates the global namespace with them.'''
for c in class_list:
globals()[c] = type(c, (parent_class,), {})
# high level constructs:
# AND condition is assumed as default. OR and NOT are explicit.
# OR is meant to be used an infix [OR between LHS and RHS of 'OR' object]
# However, it could also be implemented prefix: OR(func1, func2, func3 ...)
# Filter - encapsulates the entire set of rules to filter with.
# Field - represents each field/column
# Matcher - generic matcher to be used in combination with a field
#
build(["Filter", "Field", "Matcher"], Store)
build(["Movie", "Year", "Director", "Genre"], Field)
build(["startswith", "endswith", "has", "gt", "ge", "lt", "le", "eq"], Matcher)
# ^^ Possibilities:
# Matchers could semantically validate values passed to it
# Fields of homogenous types could restrict Matchers for it
# Ex: A field 'Movie' will allow 'startswith', 'endswith', 'has', 'eq'
# but will error if 'gt', 'lt', etc are passed
# -----------------------------------------------------------------------------
#
# The example below defines a 'filter' encompassing all rules with which
# movies should be shortlisted. A data-accessor (adapter which provides a
# segregated interface for I/O) will consume this filter and use the info
# encapsulated in it while filtering data
#
FAVOURITE = Filter(
Movie(
startswith("star")
),
Director(
has("lucas"), OR, has("abrams")
),
Year(gt(1970))
)
class MovieDataAccessor:
'''The data accessor (wrapper) which provides the interface(s) through
which information can be requested and retrieved'''
def fetch(self, fields=None, filter=None) -> list:
'''Returns list of tuples, contianing requested fields filtered
as per the supplied 'Filter' object'''
pass
print(FAVOURITE)
# Prints: Filter(Movie(startswith(star)),Director(has(lucas),OR,has(abrams)),Year(gt(1970)))
# This is useful for reading. Serialisation will require something more complex.
mda = MovieDataAccessor()
mda.fetch(fields=(Movie, Director, Year), filter=FAVOURITE)
# returns: [
# (<movie>, <director>, <year>),
# ...
# ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment