-
Notifications
You must be signed in to change notification settings - Fork 189
Add support for TIMEZONE #252
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
Conversation
d751cb4
to
51f465b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this contribution, overall looks very good.
Note that this also should be documented.
cur = trino_connection.cursor() | ||
cur.execute('SHOW TABLES FROM information_schema') | ||
cur.fetchall() | ||
assert cur._request.http_headers[constants.HEADER_TIMEZONE] == "Europe/Brussels" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of the request header testing, please check using SELECT current_timezone()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Thanks
cur = trino_connection.cursor() | ||
cur.execute('SHOW TABLES FROM information_schema') | ||
cur.fetchall() | ||
assert cur._request.http_headers[constants.HEADER_TIMEZONE] == get_localzone_name() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as in test_set_timezone_in_connection
.
tests/unit/test_client.py
Outdated
@@ -1056,3 +1060,51 @@ def test_request_headers_role_empty(mock_get_and_post): | |||
req.get("URL") | |||
_, get_kwargs = get.call_args | |||
assert_headers_with_roles(post_kwargs["headers"], None) | |||
|
|||
|
|||
def assert_headers_timezone(headers: Dict[str, str], timezone: Optional[str]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make second parameter required instead of optional and move get_localzone_name()
call to the test itself. Makes the test easier to follow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
trino/client.py
Outdated
@@ -127,6 +131,7 @@ def __init__( | |||
self._roles = roles.copy() if roles is not None else {} | |||
self._prepared_statements: Dict[str, str] = {} | |||
self._object_lock = threading.Lock() | |||
self._timezone = timezone or get_localzone_name() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might be a breaking change, In the sense that suddenly the local timezone will be used compared to UTC.
I'll leave this to the maintainers to decide if we need to put it behind a feature flag or announce it as a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, how should we proceed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Before this change the server returned values in UTC? If yes, then won't setting timezone=UTC
here explicitly preserve old behaviour? If yes then it means a workaround exists to preserve old behaviour and we can mention it in release notes.
Note that in larger picture this makes Python client match JDBC client etc. So I'd consider it a bugfix which happens to be breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current behavior is timezone=UTC
as default.
setup.py
Outdated
@@ -39,6 +39,7 @@ | |||
"pytest", | |||
"pytest-runner", | |||
"click", | |||
"tzlocal", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed as it is already included in the install_requires
.
74aa1b4
to
7972aab
Compare
Done the doc. Thanks for your review and comments @mdesmet ! |
7972aab
to
5e7f926
Compare
README.md
Outdated
@@ -357,6 +357,20 @@ conn = trino.dbapi.connect( | |||
) | |||
``` | |||
|
|||
## Timezone | |||
|
|||
Sets the time zone for the session using the time zone name. Defaults to the timezone set on your workstation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sets the time zone for the session using the time zone name. Defaults to the timezone set on your workstation. | |
The time zone for the session can be explicitly set using the official IANA time zone name. When not set the time zone defaults to the client side local timezone. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Thanks
trino/client.py
Outdated
@@ -127,6 +131,7 @@ def __init__( | |||
self._roles = roles.copy() if roles is not None else {} | |||
self._prepared_statements: Dict[str, str] = {} | |||
self._object_lock = threading.Lock() | |||
self._timezone = timezone or get_localzone_name() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
session_tz = res[0][0] | ||
localzone = get_localzone_name() | ||
assert session_tz == localzone or \ | ||
(session_tz == "UTC" and localzone == "Etc/UTC") \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I conclude from this test if we set the timezone header to "Etc/UTC", Trino actually understands this but returns UTC as the current_timezone()
.
Is my understanding correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly
trino/client.py
Outdated
@@ -127,6 +131,7 @@ def __init__( | |||
self._roles = roles.copy() if roles is not None else {} | |||
self._prepared_statements: Dict[str, str] = {} | |||
self._object_lock = threading.Lock() | |||
self._timezone = timezone or get_localzone_name() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a way to validate the time zone using tzlocal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I added the check with ZoneInfo
1bf9336
to
423e360
Compare
@ulisesojeda : Please don't merge master into your branch but rebase on master. |
e611269
to
0acb5e7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for implementing this @ulisesojeda. I've started reviewing this and sorry about the delays here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thanks for a lot for adding this support.
Left some comments.
tests/unit/test_client.py
Outdated
@@ -47,6 +48,12 @@ | |||
_RetryWithExponentialBackoff, | |||
) | |||
|
|||
try: | |||
from zoneinfo._common import ZoneInfoNotFoundError # type: ignore |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can directly import zoneinfo.ZoneInfoNotFoundError
- that's the public location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as per https://pypi.org/project/backports.zoneinfo/, with need to import the backport for Python <3.9. Did I miss something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, good point. I was checking with 3.10 only.
Can you add a TODO comment that it can be simplified once we move past 3.9?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I was saying something like this:
try:
from zoneinfo import ZoneInfoNotFoundError
except ModuleNotFoundError:
from backports.zoneinfo._common import ZoneInfoNotFoundError
@@ -127,6 +138,9 @@ def __init__( | |||
self._roles = roles.copy() if roles is not None else {} | |||
self._prepared_statements: Dict[str, str] = {} | |||
self._object_lock = threading.Lock() | |||
if timezone: # Check timezone validity | |||
ZoneInfo(timezone) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for other reviewers this throws on invalid timezones (invalidity is determined by the OS's tzdb files or the tzdata package shipped by Python itself):
ZoneInfoNotFoundError: 'No time zone found with key Foobar'
5853f72
to
70d59e7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM % some comments.
Thanks for this.
I plan to not merge this for 0.320.0 so that other features can be part of 0.320.0 which people might be waiting for. Then 0.321.0 can introduce this change. I hope that's ok?
While there's a way to preserve old beahviour it requires someone to read release notes so I'd like to announce on Trino community before this change.
.pre-commit-config.yaml
Outdated
@@ -13,9 +13,4 @@ repos: | |||
additional_dependencies: | |||
- "types-pytz" | |||
- "types-requests" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The isort section should not be removed. Can you revert this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch. I didn't notice this. 🙈
@@ -79,8 +79,8 @@ | |||
"Programming Language :: Python :: Implementation :: PyPy", | |||
"Topic :: Database :: Front-Ends", | |||
], | |||
python_requires=">=3.7", | |||
install_requires=["pytz", "requests"], | |||
python_requires='>=3.7', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please revert the unrelated change on python_requires
Sure. Thanks for the review @hashhar |
70d59e7
to
bc78644
Compare
Rebased. Will merge once CI is done. |
Co-authored-by: Ulises <[email protected]> Co-authored-by: Ashhar Hasan <[email protected]>
bc78644
to
03f2670
Compare
Add support for TIMEZONE
Fixes #27