Skip to content

Commit

Permalink
chore: support 3.11 (#334)
Browse files Browse the repository at this point in the history
* chore: support 3.11

* Bump greenlet for Python 3.11

* Fix lint

* Fix lint, perhaps

* More lint fixing
  • Loading branch information
betodealmeida authored Mar 21, 2023
1 parent f96317a commit 0f00328
Show file tree
Hide file tree
Showing 18 changed files with 55 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.8', '3.9', '3.10']
python-version: ['3.8', '3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Next
- Fix for escaping identifiers correctly (#340)
- Support for S3-compatible storage (#343)
- Adapters can now know which columns were requested (#345)
- Python 3.11 officially supported (#334)

Version 1.2.0 - 2023-02-17
==========================
Expand Down
15 changes: 1 addition & 14 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Thanks for wanting to contribute to the project! Here's a quick rundown of how t
Install ``pyenv``
=================

You don't *need* ``pyenv``, but development will be easier with it. Follow `these instructions <https://github.com/pyenv/pyenv#installation>`_ to install it, and then create a virtual environment. Shillelagh is tested with Python 3.8-3.10, so make sure to install one of those versions.
You don't *need* ``pyenv``, but development will be easier with it. Follow `these instructions <https://github.com/pyenv/pyenv#installation>`_ to install it, and then create a virtual environment. Shillelagh is tested with Python 3.8-3.11, so make sure to install one of those versions.

.. code-block:: bash
Expand All @@ -25,19 +25,6 @@ You want to install the package in developer mode (``-e``) with all the dependen
$ pip install -e ".[testing]"
Install the latest ``apsw``
===========================

``apsw`` (another Python SQLite wrapper) is the Python library that gives Shillelagh its superpowers. The last command will install a version of ``apsw`` that is not official, since the package is not officially published to PyPI. You should compile and install the latest version from source code by running:

.. code-block:: bash
$ export VERSION=3.38.1
$ export RELEASE=r1
$ pip install https://github.com/rogerbinns/apsw/releases/download/${VERSION}-${RELEASE}/apsw-${VERSION}-${RELEASE}.zip \
> --global-option=fetch --global-option=--version --global-option=${VERSION} --global-option=--all \
> --global-option=build --global-option=--enable-all-extensions
Install pre-commit hooks
========================

Expand Down
2 changes: 1 addition & 1 deletion examples/weatherapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
three_days_ago = datetime.now() - timedelta(days=3)

# sign up for an API key at https://www.weatherapi.com/my/
api_key = sys.argv[1]
api_key = sys.argv[1] # pylint: disable=invalid-name

connection = connect(":memory:")
cursor = connection.cursor()
Expand Down
6 changes: 4 additions & 2 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ certifi==2022.6.15
# via requests
charset-normalizer==2.1.0
# via requests
greenlet==1.1.2
# via sqlalchemy
greenlet==2.0.2
# via
# shillelagh
# sqlalchemy
idna==3.3
# via requests
packaging==23.0
Expand Down
16 changes: 10 additions & 6 deletions requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ appdirs==1.4.4
# via shillelagh
apsw==3.38.5.post1
# via shillelagh
astroid==2.11.7
astroid==2.14.2
# via pylint
attrs==21.4.0
# via
Expand Down Expand Up @@ -41,8 +41,10 @@ codespell==2.1.0
# via shillelagh
coverage[toml]==6.4.2
# via pytest-cov
dill==0.3.5.1
# via pylint
dill==0.3.6
# via
# pylint
# shillelagh
distlib==0.3.5
# via virtualenv
exceptiongroup==1.0.4
Expand All @@ -53,8 +55,10 @@ freezegun==1.2.1
# via shillelagh
google-auth==2.9.1
# via shillelagh
greenlet==1.1.2
# via sqlalchemy
greenlet==2.0.2
# via
# shillelagh
# sqlalchemy
html5lib==1.1
# via shillelagh
identify==2.5.2
Expand Down Expand Up @@ -118,7 +122,7 @@ pyfakefs==4.6.3
# via shillelagh
pygments==2.12.0
# via shillelagh
pylint==2.14.5
pylint==2.16.2
# via shillelagh
pyparsing==3.0.9
# via packaging
Expand Down
7 changes: 5 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ classifiers =
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: SQL


Expand All @@ -44,6 +45,7 @@ install_requires =
python_dateutil>=2.8.1
requests>=2.25.1
sqlalchemy>=1.3
greenlet>=2.0.2 # needed for Python 3.11 w/o memory leak
typing_extensions>=3.7.4.3
packaging
# The usage of test_requires is discouraged, see `Dependency Management` docs
Expand Down Expand Up @@ -71,6 +73,7 @@ testing =
beautifulsoup4>=4.11.1
boto3>=1.24.28
codespell>=2.1.0
dill>=0.3.6
freezegun>=1.1.0
google-auth>=1.23.0
html5lib>=1.1
Expand All @@ -82,7 +85,7 @@ testing =
psutil>=5.8.0
pyfakefs>=4.3.3
pygments>=2.8
pylint>=2.11.1
pylint>=2.16.2
pytest-cov>=2.11.1
pytest-integration==0.2.2
pytest-mock>=3.5.1
Expand All @@ -109,7 +112,7 @@ all =
psutil>=5.8.0
pyfakefs>=4.3.3
pygments>=2.8
pylint>=2.11.1
pylint>=2.16.2
pytest-cov>=2.11.1
pytest-integration==0.2.2
pytest-mock>=3.5.1
Expand Down
1 change: 1 addition & 0 deletions src/shillelagh/adapters/api/gsheets/parsing/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def consume(cls, pattern: str) -> Tuple["Token", str]:
"""
match = re.match(cls.regex, pattern)
if not match:
# pylint: disable=broad-exception-raised
raise Exception("Token could not find match")
token = match.group()
return cls(token), pattern[len(token) :]
Expand Down
4 changes: 3 additions & 1 deletion src/shillelagh/adapters/api/gsheets/parsing/date.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# pylint: disable=invalid-name, fixme
"""
Parse and format Google Sheet date/time patterns.
https://developers.google.com/sheets/api/guides/formats?hl=en#date_and_time_format_patterns
"""

# pylint: disable=invalid-name, fixme, broad-exception-raised

import calendar
import re
from collections import defaultdict
Expand Down
2 changes: 1 addition & 1 deletion src/shillelagh/adapters/api/gsheets/parsing/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
https://developers.google.com/sheets/api/guides/formats#number_format_tokens
"""
# pylint: disable=c-extension-no-member
# pylint: disable=c-extension-no-member, broad-exception-raised
import math
import operator
import re
Expand Down
2 changes: 1 addition & 1 deletion src/shillelagh/adapters/api/weatherapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def combine_time_filters(bounds: Dict[str, Filter]) -> Range:
raise ImpossibleFilterError()

if not isinstance(time_range, Range) or not isinstance(time_epoch_range, Range):
raise Exception("Invalid filter")
raise Exception("Invalid filter") # pylint: disable=broad-exception-raised

# convert time_epoch range to datetime so we can combine it
# with the time range
Expand Down
10 changes: 8 additions & 2 deletions src/shillelagh/adapters/file/csvfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import os
import tempfile
import urllib.parse
from datetime import timedelta
from pathlib import Path
from typing import Any, Dict, Iterator, List, Optional, Tuple, cast

Expand All @@ -40,6 +41,8 @@
FILTERING_COST = 1000
SORTING_COST = 10000

DEFAULT_TIMEOUT = timedelta(minutes=3)

SUPPORTED_PROTOCOLS = {"http", "https"}


Expand Down Expand Up @@ -116,7 +119,7 @@ def supports(uri: str, fast: bool = True, **kwargs: Any) -> MaybeType:
if fast:
return Maybe

response = requests.head(uri)
response = requests.head(uri, timeout=DEFAULT_TIMEOUT.total_seconds())
return "text/csv" in response.headers.get("content-type", "")

@staticmethod
Expand All @@ -134,7 +137,10 @@ def __init__(self, path_or_uri: str):

# download CSV file
with tempfile.NamedTemporaryFile(delete=False) as output:
response = requests.get(path_or_uri)
response = requests.get(
path_or_uri,
timeout=DEFAULT_TIMEOUT.total_seconds(),
)
output.write(response.content)
path = Path(output.name)

Expand Down
5 changes: 5 additions & 0 deletions src/shillelagh/backends/apsw/dialects/gsheets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""
import logging
import urllib.parse
from datetime import timedelta
from operator import itemgetter
from typing import Any, Dict, List, Optional, Tuple, cast

Expand All @@ -22,6 +23,9 @@
_logger = logging.getLogger(__name__)


DEFAULT_TIMEOUT = timedelta(minutes=3)


class QueryType(TypedDict, total=False):
"""
Types for parameters in the SQLAlchemy URI query.
Expand Down Expand Up @@ -119,6 +123,7 @@ def do_ping(self, dbapi_connection: _ConnectionFairy) -> bool:
"""
response = requests.get(
"https://www.google.com/appsstatus/dashboard/incidents.json",
timeout=DEFAULT_TIMEOUT.total_seconds(),
)
payload = response.json()

Expand Down
3 changes: 3 additions & 0 deletions src/shillelagh/backends/apsw/vt.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def get_all_bounds(
constraintargs,
):
if sqlite_index_constraint not in operator_map:
# pylint: disable=broad-exception-raised
raise Exception(f"Invalid constraint passed: {sqlite_index_constraint}")
if column_index == LIMIT_OFFSET_INDEX:
continue
Expand Down Expand Up @@ -212,6 +213,7 @@ def get_limit_offset(
constraintargs,
):
if sqlite_index_constraint not in operator_map:
# pylint: disable=broad-exception-raised
raise Exception(f"Invalid constraint passed: {sqlite_index_constraint}")
if column_index != LIMIT_OFFSET_INDEX:
continue
Expand Down Expand Up @@ -256,6 +258,7 @@ def get_bounds(
bounds[column_name] = class_.build(operations)
break
else:
# pylint: disable=broad-exception-raised
raise Exception("No valid filter found")

return bounds
Expand Down
1 change: 1 addition & 0 deletions src/shillelagh/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def get_endpoints_from_operation(
if operator == Operator.LT:
return Endpoint(None, True, Side.LEFT), Endpoint(value, False, Side.RIGHT)

# pylint: disable=broad-exception-raised
raise Exception(f"Invalid operator: {operator}")


Expand Down
3 changes: 3 additions & 0 deletions src/shillelagh/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class RowIDManager:

def __init__(self, ranges: List[range]):
if not ranges:
# pylint: disable=broad-exception-raised
raise Exception("Argument ``ranges`` cannot be empty")

self.ranges = ranges
Expand All @@ -94,6 +95,7 @@ def check_row_id(self, row_id: int) -> None:
"""
for range_ in self.ranges:
if range_.start <= row_id < range_.stop:
# pylint: disable=broad-exception-raised
raise Exception(f"Row ID {row_id} already present")

def insert(self, row_id: Optional[int] = None) -> int:
Expand Down Expand Up @@ -132,6 +134,7 @@ def delete(self, row_id: int) -> None:

return

# pylint: disable=broad-exception-raised
raise Exception(f"Row ID {row_id} not found")


Expand Down
1 change: 1 addition & 0 deletions tests/fakes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Fake objects to simplify testing.
"""

import json
import os
import urllib.parse
Expand Down
8 changes: 5 additions & 3 deletions tests/fields_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
Tests for shillelagh.fields.
"""
import datetime
import sys
from typing import Union

import pytest
Expand Down Expand Up @@ -500,9 +501,10 @@ def test_fastisodatetime() -> None:
0,
)

with pytest.raises(ProgrammingError) as excinfo:
FastISODateTime().parse("2020-01-01T12:00Z")
assert str(excinfo.value) == 'Unable to parse "2020-01-01T12:00Z"'
if sys.version_info < (3, 11):
with pytest.raises(ProgrammingError) as excinfo:
FastISODateTime().parse("2020-01-01T12:00Z")
assert str(excinfo.value) == 'Unable to parse "2020-01-01T12:00Z"'

with pytest.raises(ProgrammingError) as excinfo:
FastISODateTime().parse("invalid")
Expand Down

0 comments on commit 0f00328

Please sign in to comment.