Skip to content

Commit

Permalink
Dependencies/python3.12 (#179)
Browse files Browse the repository at this point in the history
* Upgrade Python from 3.11 to 3.12

* Revert unsupported target version

* Test python3.12

* Change target-version

* Replace pre-commit double-quote-string-fixer hook with ruff rule

* Lint

* Replace uwsgi with gunicorn because of unbit/uwsgi#2566
  • Loading branch information
sixcare authored Oct 31, 2023
1 parent 621144a commit 0db7f6c
Show file tree
Hide file tree
Showing 15 changed files with 47 additions and 48 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Set up Python 3.11
- name: Set up Python 3.12
uses: actions/setup-python@v4
with:
python-version: "3.11"
python-version: "3.12"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
Expand Down Expand Up @@ -48,7 +48,7 @@ jobs:
strategy:
matrix:
python-version: ['3.11']
python-version: ['3.11', '3.12']

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ repos:
- id: check-merge-conflict
- id: check-yaml
- id: debug-statements
- id: double-quote-string-fixer
- id: fix-encoding-pragma
args: ['--remove']
- id: name-tests-test
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.11-slim-bullseye
FROM python:3.12-slim-bullseye

# Set work directory
WORKDIR /turplanlegger
Expand All @@ -17,7 +17,7 @@ RUN apt-get update && \
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=5 CMD [ "curl", "-fs", "http://localhost:5000/test"]

# Default command
CMD ["uwsgi", "--http", "0.0.0.0:5000", "--module", "wsgi:app", "--processes", "4", "--threads", "2"]
CMD ["gunicorn", "--bind=0.0.0.0:5000", "--workers=2", "--threads=4", "--worker-class=gthread", "--log-file=-", "wsgi:app"]

# Expose port
EXPOSE 5000
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ dev = [
"ruff==v0.1.3"
]
prod = [
"uwsgi==2.0.22",
"gunicorn[gthread]==21.2.0",
"psycopg[c]==3.1.12"
]

Expand All @@ -67,7 +67,7 @@ no-cov = "cov --no-cov {args}"
lint = "pre-commit run -a --show-diff-on-failure"

[[tool.hatch.envs.test.matrix]]
python = ["310", "311"]
python = ["310", "311", "312"]

[tool.coverage.run]
branch = true
Expand All @@ -90,9 +90,9 @@ include = [
]

[tool.ruff]
select = ["E", "W", "I", "F", "YTT", "C4", "T10", "ISC", "ICN", "RSE"]
select = ["E", "W", "Q", "I", "F", "YTT", "C4", "T10", "ISC", "ICN", "RSE"]
line-length = 120
target-version = "py311"
target-version = "py312"

[tool.ruff.flake8-quotes]
inline-quotes = "single"
2 changes: 1 addition & 1 deletion tests/test_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def test_add_route_no_geometry(self):

data = json.loads(response.data.decode('utf-8'))
self.assertEqual(data['title'], 'Failed to parse route')
self.assertEqual(data['detail'], 'Missing mandatory field \'route\'')
self.assertEqual(data['detail'], "Missing mandatory field 'route'")
self.assertEqual(data['type'], 'about:blank')
self.assertEqual(data['instance'], 'http://localhost/routes')

Expand Down
6 changes: 3 additions & 3 deletions tests/test_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def setUpClass(cls):
'last_name': 'Smart',
'email': 'petter@smart.com',
'auth_method': 'basic',
'password': 'GbYRCzE}q:~e6Qo?\':fg^*:d6;{*NV&b=Q2GUAqYv#792C<{?,8@JoYX>qV)3H^q',
'password': "GbYRCzE}q:~e6Qo?':fg^*:d6;{*NV&b=Q2GUAqYv#792C<{?,8@JoYX>qV)3H^q",
'private': False
}
cls.user7 = {
Expand All @@ -81,7 +81,7 @@ def setUpClass(cls):
'last_name': 'Smart',
'email': 'petter@smart.com',
'auth_method': 'basic',
'password': 'm9uMSpb&q.Ft,[5,%oWj7yk-$YFBvKd}J<fNrToR2x~&+d_9J}K:gcGmUq#qkL\'#',
'password': "m9uMSpb&q.Ft,[5,%oWj7yk-$YFBvKd}J<fNrToR2x~&+d_9J}K:gcGmUq#qkL'#",
'private': False
}
cls.user8 = {
Expand Down Expand Up @@ -238,7 +238,7 @@ def test_create_user_no_last_name(self):

data = json.loads(response.data.decode('utf-8'))
self.assertEqual(data['title'], 'Failed to parse user')
self.assertEqual(data['detail'], 'Missing mandatory field \'last_name\'')
self.assertEqual(data['detail'], "Missing mandatory field 'last_name'")
self.assertEqual(data['type'], 'about:blank')
self.assertEqual(data['instance'], 'http://localhost/users')

Expand Down
2 changes: 1 addition & 1 deletion turplanlegger/auth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ def before_request():
and not request.is_json):
raise ApiProblem(
'Request has wrong Content-Type',
'PATCH, POST and PUT requests must set \'Content-type\' to \'application/json\'',
"PATCH, POST and PUT requests must set 'Content-type' to 'application/json'",
415
)
8 changes: 4 additions & 4 deletions turplanlegger/auth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@


def hash_password(provided_pw: str) -> bytes:
'''Hashes a password using bcrypt.hashpw
"""Hashes a password using bcrypt.hashpw
Args:
provided_pw (str): Password to encrypt
Returns:
Encoded password (bytes)
'''
"""
return bcrypt.hashpw(provided_pw.encode('utf-8'), bcrypt.gensalt())


def check_password(hashed_pw: str, provided_pw: str) -> bool:
'''Checks for password match using bcrypt.checkpw
"""Checks for password match using bcrypt.checkpw
Args:
hashed_pw (str): hashed password (from db)
provided_pw (str): password provided by user
Returns:
bool
'''
"""
return bcrypt.checkpw(provided_pw.encode('utf-8'), hashed_pw.encode('utf-8'))
8 changes: 4 additions & 4 deletions turplanlegger/models/note.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ class Note:

def __init__(self, owner: str, content: str, **kwargs) -> None:
if not owner:
raise ValueError('Missing mandatory field \'owner\'')
raise ValueError("Missing mandatory field 'owner'")
if not isinstance(owner, str):
raise TypeError('\'owner\' must be str')
raise TypeError("'owner' must be str")
if not content:
raise ValueError('Missing mandatory field \'content\'')
raise ValueError("Missing mandatory field 'content'")
if not isinstance(content, str):
raise TypeError('\'content\' must be string')
raise TypeError("'content' must be string")

self.owner = owner
self.content = content
Expand Down
6 changes: 3 additions & 3 deletions turplanlegger/models/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class Route:

def __init__(self, owner: str, route: JSON, **kwargs) -> None:
if not owner:
raise ValueError('Missing mandatory field \'owner\'')
raise ValueError("Missing mandatory field 'owner'")
if not isinstance(owner, str):
raise TypeError('\'owner\' must be string')
raise TypeError("'owner' must be string")
if not route:
raise ValueError('Missing mandatory field \'route\'')
raise ValueError("Missing mandatory field 'route'")

self.owner = owner
self.route = route
Expand Down
8 changes: 4 additions & 4 deletions turplanlegger/models/trip.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ class Trip:

def __init__(self, owner: str, name: str, **kwargs) -> None:
if not owner:
raise ValueError('Missing mandatory field \'owner\'')
raise ValueError("Missing mandatory field 'owner'")
if not isinstance(owner, str):
raise TypeError('\'owner\' must be string')
raise TypeError("'owner' must be string")
if not name:
raise ValueError('Missing mandatory field \'name\'')
raise ValueError("Missing mandatory field 'name'")
if not isinstance(name, str):
raise TypeError('\'name\' must be string')
raise TypeError("'name' must be string")

if name is not None and len(name) > 512:
raise ValueError("'name' is too long")
Expand Down
16 changes: 8 additions & 8 deletions turplanlegger/models/trip_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ class TripDate:

def __init__(self, owner: str, start_time: datetime, end_time: datetime, **kwargs) -> None:
if not owner:
raise ValueError('Missing mandatory field \'owner\'')
raise ValueError("Missing mandatory field 'owner'")
if not isinstance(owner, str):
raise TypeError('\'owner\' must be string')
raise TypeError("'owner' must be string")
if not isinstance(start_time, datetime):
raise TypeError('\'start_time\' must be an datetime instance')
raise TypeError("'start_time' must be an datetime instance")
if not isinstance(end_time, datetime):
raise TypeError('\'end_time\' must be an datetime instance')
raise TypeError("'end_time' must be an datetime instance")

self.owner = owner
self.start_time = start_time
Expand Down Expand Up @@ -77,19 +77,19 @@ def parse(cls, json: JSON) -> 'TripDate':
"""
start_time = json.get('start_time', None)
if start_time is None:
raise ValueError('Missing mandatory field \'start_time\'')
raise ValueError("Missing mandatory field 'start_time'")
try:
start_time = datetime.fromisoformat(start_time)
except ValueError:
raise ValueError('Field \'end_time\' must be ISO 8601 date as tring')
raise ValueError("Field 'end_time' must be ISO 8601 date as tring")

end_time = json.get('end_time', None)
if end_time is None:
raise ValueError('Missing mandatory field \'end_time\'')
raise ValueError("Missing mandatory field 'end_time'")
try:
end_time = datetime.fromisoformat(end_time)
except ValueError:
raise ValueError('Field \'end_time\' must be ISO 8601 date as string')
raise ValueError("Field 'end_time' must be ISO 8601 date as string")

if start_time > end_time:
raise ValueError('start_time can not be before end_time')
Expand Down
16 changes: 8 additions & 8 deletions turplanlegger/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,25 @@ def __init__(self, name: str, last_name: str, email: str, auth_method: str,
password: str, private: bool = False, **kwargs) -> None:

if not isinstance(private, bool):
raise TypeError('\'private\' must be boolean')
raise TypeError("'private' must be boolean")

if not name:
raise ValueError('Missing mandatory field \'name\'')
raise ValueError("Missing mandatory field 'name'")
if not isinstance(name, str):
raise TypeError('\'name\' must be string')
raise TypeError("'name' must be string")

if not last_name:
raise ValueError('Missing mandatory field \'last_name\'')
raise ValueError("Missing mandatory field 'last_name'")
if not isinstance(last_name, str):
raise TypeError('\'last_name\' must be string')
raise TypeError("'last_name' must be string")

if not email:
raise ValueError('Missing mandatory field \'email\'')
raise ValueError("Missing mandatory field 'email'")
if not isinstance(email, str):
raise TypeError('\'email\' must be string')
raise TypeError("'email' must be string")

if not auth_method:
raise ValueError('Missing mandatory field \'auth_method\'')
raise ValueError("Missing mandatory field 'auth_method'")

self.id = kwargs.get('id') or str(uuid4())
self.name = name
Expand Down
2 changes: 1 addition & 1 deletion turplanlegger/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def before_request():
and not request.is_json):
raise ApiProblem(
'Request has wrong Content-Type',
'PATCH, POST and PUT requests must set \'Content-type\' to \'application/json\'',
"PATCH, POST and PUT requests must set 'Content-type' to 'application/json'",
415
)

Expand Down
2 changes: 1 addition & 1 deletion turplanlegger/views/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def lookup_user():
raise ApiProblem('Failed to get user', str(e), 400)

if not email:
raise ApiProblem('Failed to get user', 'Provide \'email\' in JSON body', 400)
raise ApiProblem('Failed to get user', "Provide 'email' in JSON body", 400)

try:
user = User.find_by_email(email)
Expand Down

0 comments on commit 0db7f6c

Please sign in to comment.