Skip to content

Conversation

@michaelkerry
Copy link
Contributor

@michaelkerry michaelkerry commented Mar 17, 2021

The idea here is to provide a pluggable collection of utils to enable easy interactions with databases of various types.

I've started with oracle db bc the example project into which this package is integrated (a flask api example project) uses an oracle db, but the intent is to enable additional modules for mysql, postgres, etc.

I've updated the documentation in a way that I think makes sense - I was able to start a python console session and follow those instructions (with valid connection information) and pull back results. Please let me know what else might be helpful. One section to be added at beginning: 'Purpose and Intended Audience'

README.md Outdated

To query the `EMP` table for all records:

result = db.execute_query("select * from emp")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap code examples in ticks.

I'd also like to see a super streamlined example of it put together like:

from pydb import OracleDb
from pylog import logger

db = OracleDb(host="valid_host", port=8003, service="SERVICE_NAME", user="username", pwd="pwd")
result = db.execute_query("select * from emp")
logger.info(json.dumps(result))

db.cleanup()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code wrapped; integrated example added

Copy link
Collaborator

@ccurreri ccurreri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the individual comments. Some small design things that seems strange to me, but might make sense in your context. I only commented on the first instances that I saw but some of them are done multiple times.

Chiefly, you have a helper method that gets the connection pool but also expose it as a public attribute. You may want to consider refactoring to make it more clear that there is only one way that folks should be interacting with the pool object.

pydb/database.py Outdated
Comment on lines 14 to 29
class DBInterface(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
return (hasattr(subclass, 'execute_query') and
callable(subclass.execute_query) and
hasattr(subclass, 'execute_update') and
callable(subclass.execute_update) and
hasattr(subclass, 'health_check') and
callable(subclass.health_check) and
hasattr(subclass, 'cleanup') and
callable(subclass.cleanup) and
hasattr(subclass, 'create_connection') and
callable(subclass.create_connection) and
hasattr(subclass, 'get_session') and
callable(subclass.get_session)
or NotImplemented)
Copy link
Collaborator

@ccurreri ccurreri Mar 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason you're overriding this?

You already are handling this with the abstractmethod decorators. If a subclass fails to implement one of these it can't be instantiated.

>>> from abc import ABC, abstractmethod
>>>
>>> class Foo(ABC):
...     @abstractmethod
...     def bar(self):
...             pass
...
>>> class Baz(Foo):
...     pass
...
>>> x = Baz()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Baz with abstract method bar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated - removed

Comment on lines 80 to 83
if self.pool:
return self.pool
else:
self.set_up_session_pool()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else clause here technically will never fire because you always set self.pool in __init__ or throw an error.

One alternative would be to just use a property for this to load it on demand:

class OracleDB(DBInterface):
    def __init__(self, host: str, port: int, service: str, user: str, pwd: str,
                       logging_level: int = 50, logging_format: logging.Formatter = None):

        . . .

        self._pool = None

        . . .
        
    @property
    def pool(self):
        if not self._pool:
           self._pool = self.set_up_session_pool()

        return self._pool

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment on lines 90 to 91
connection = self.get_session_pool().acquire()
return connection
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just be:

return self.get_session_pool().acquire()

or with the changes above just:

return self.pool.acquire()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated


def create_row(*args):
return dict(zip(column_names, args))
return create_row
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean return create_row() here or do you actually mean to return the function?

If the latter this is something a lambda could do relatively easily:

return lambda *args: dict(zip(column_names, args))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I 'inherited' this syntax - and am opting to leave as is since it is working as expected

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants