Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for connection.raw_connection #58

Merged
merged 7 commits into from
Mar 4, 2019

Conversation

gvbgduh
Copy link
Member

@gvbgduh gvbgduh commented Mar 3, 2019

Adds ability to access the underlying driver's connection interface.

@gvbgduh
Copy link
Member Author

gvbgduh commented Mar 3, 2019

I followed 2 different approaches:

  1. Pass the target method as str to the generic method like raw_api_call, so the driver can be called as getattr(obj, str). While it saves some lines for implementation it can introduced unacceptable complexity for generalised solution. The source of problem that mysql and sqlite (and asyncpg in case of explicit cursor) require two actions to preform the query. It might also lead to a dead-end.
  2. Just exposing the raw backend connection. It gives full flexibility and control, but requires more verbose implementation, which is probably ok for such cases.

If anything of these looks good I can get rid of redundant bits in the code and finish the rest of unittests. I'm also quite bad at naming.
Any feedback would be much appreciated.

@gvbgduh
Copy link
Member Author

gvbgduh commented Mar 3, 2019

Yeah, p. 2 (expose_backend_connection) looks somehow neat without interfering with existing, but might require some attention for releasing connections back to pool.

@gvbgduh gvbgduh marked this pull request as ready for review March 3, 2019 22:05
@gvbgduh gvbgduh changed the title Rough implementation of the raw driver call An approach for accessing the raw sql driver Mar 4, 2019
def __getitem__(self, key: str) -> typing.Any:
idx, datatype = self._column_map[key]
raw = self._row[idx]
def __getitem__(self, key: typing.Any) -> typing.Any:
Copy link
Member Author

Choose a reason for hiding this comment

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

Not happy with this implementation in general and especially passing typing.Any. I think it might be limited to str and int as a raw record's fields can be accessed by an int index or by the str key.

@gvbgduh gvbgduh changed the title An approach for accessing the raw sql driver Support for connection.raw_connection to access the underlying driver's connection interface Mar 4, 2019
@gvbgduh gvbgduh changed the title Support for connection.raw_connection to access the underlying driver's connection interface Support for connection.raw_connection Mar 4, 2019
Copy link
Member

@tomchristie tomchristie left a comment

Choose a reason for hiding this comment

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

Crackin' stuff. A few comments here - most importantly, tweaking things so that .raw_connection is a property.

@@ -150,6 +150,10 @@ def __init__(self, database: PostgresBackend, dialect: Dialect):
async for row in self._connection.cursor(query, *args):
yield Record(row, result_columns, self._dialect)

async def raw_connection(self) -> asyncpg.connection.Connection:
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need to (shouldn't) be an async method. (It's not performing any I/O itself, just returning the handle)

I guess we should probably use a property too, rather than a method.

@property
def raw_connection(self):
    ...

Copy link
Member

Choose a reason for hiding this comment

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

It's possible that the implementations ought to use a def get_raw_connection() method, and the developer facing interface should be a def raw_connection property, that calls get_raw_connection()

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah, was thinking about the property as well, and naming would be more natural as well.

@@ -111,6 +111,10 @@ def __init__(
async for record in connection.iterate(query):
yield record

async def raw_connection(self) -> typing.Any:
async with self.connection() as connection:
return await connection.raw_connection()
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't be exposed at the top-level. Ie. You have to acquire the connection first, in order to access .raw_connection

Copy link
Member Author

Choose a reason for hiding this comment

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

That makes sense and sets explicit flow.

Copy link
Member Author

Choose a reason for hiding this comment

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

That also gives clarity. As connection already acquired (and it seems reasonable to avoid spawning the raw_connection from the database level acquiring a new one) we can expose already allocated connection object, that makes it a perfect case for the property.

@@ -168,6 +172,9 @@ def __init__(self, backend: DatabaseBackend) -> None:
async def execute_many(self, query: ClauseElement, values: list) -> None:
await self._connection.execute_many(query, values)

async def raw_connection(self) -> typing.Any:
return await self._connection.raw_connection()
Copy link
Member

Choose a reason for hiding this comment

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

Let's go with...

@property
def raw_connection(self) -> typing.Any:
    return self._connection.get_raw_connection()

Copy link
Member

Choose a reason for hiding this comment

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

Also let's move the implementation to the very end of the class. That layout feels more natural to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

Happy with layout, but considering the given context I think I'm looking forward to:

@property
def raw_connection(self) -> typing.Any:
    return self._connection.raw_connection

if there're no objections

Copy link
Member

Choose a reason for hiding this comment

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

That's fine with me, yup. 👍

raw_connection_2 = await connection_2.raw_connection()

assert raw_connection_0 is raw_connection_1 is raw_connection_2
assert raw_connection_0 is connection_1._connection._connection
Copy link
Member

Choose a reason for hiding this comment

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

Using properties I think we'd really want to change this to something like this...

    async with Database(database_url) as database:
        async with database.connection() as connection_1:
            async with database.connection() as connection_2:
                assert connection_1 is connection_2
                assert connection_1.raw_connection is connection_2.raw_connection

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, that would be neat

async with Database(database_url) as database:
async with database.transaction(force_rollback=True):
# Get the raw connection
con = await database.raw_connection()
Copy link
Member

Choose a reason for hiding this comment

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

Let's use something like connection, raw_connection, or driver rather than the shorthand variable name.

@gvbgduh gvbgduh mentioned this pull request Mar 4, 2019
@tomchristie
Copy link
Member

Looks great! I don't think there's anything else to do on this part right? You happy for it to go in now?

@gvbgduh
Copy link
Member Author

gvbgduh commented Mar 4, 2019

Yeah, I'm quite happy now, can't think of anything else as well. Thanks for the feedback!

@tomchristie tomchristie merged commit cf0b368 into encode:master Mar 4, 2019
@gvbgduh
Copy link
Member Author

gvbgduh commented Mar 4, 2019

I'll prepare the documentation examples tomorrow, but within the
#57

@tomchristie
Copy link
Member

@gvbgduh Makes sense - thanks.
Released now, as 0.1.10.

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.

2 participants