mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-14 21:43:01 +00:00
avoid deprecated datetime.utcnow
deprecated in Python 3.12 replace with equivalent utils.utcnow(with_tz=False)
This commit is contained in:
@@ -12,17 +12,21 @@ branch_labels = None
|
|||||||
depends_on = None
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
|
|
||||||
|
def utcnow():
|
||||||
|
return datetime.now(timezone.utc)._replace(tzinfo=None)
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
op.add_column('users', sa.Column('created', sa.DateTime, nullable=True))
|
op.add_column('users', sa.Column('created', sa.DateTime, nullable=True))
|
||||||
c = op.get_bind()
|
c = op.get_bind()
|
||||||
# fill created date with current time
|
# fill created date with current time
|
||||||
now = datetime.utcnow()
|
now = utcnow()
|
||||||
c.execute(
|
c.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE users
|
UPDATE users
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
from urllib.parse import parse_qsl, quote, urlencode, urlparse, urlunparse
|
from urllib.parse import parse_qsl, quote, urlencode, urlparse, urlunparse
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ from oauthlib import oauth2
|
|||||||
from tornado import web
|
from tornado import web
|
||||||
|
|
||||||
from .. import orm, roles, scopes
|
from .. import orm, roles, scopes
|
||||||
from ..utils import get_browser_protocol, token_authenticated
|
from ..utils import get_browser_protocol, token_authenticated, utcnow
|
||||||
from .base import APIHandler, BaseHandler
|
from .base import APIHandler, BaseHandler
|
||||||
|
|
||||||
|
|
||||||
@@ -39,7 +38,7 @@ class TokenAPIHandler(APIHandler):
|
|||||||
self.parsed_scopes = scopes.parse_scopes(self.expanded_scopes)
|
self.parsed_scopes = scopes.parse_scopes(self.expanded_scopes)
|
||||||
|
|
||||||
# record activity whenever we see a token
|
# record activity whenever we see a token
|
||||||
now = orm_token.last_activity = datetime.utcnow()
|
now = orm_token.last_activity = utcnow(with_tz=False)
|
||||||
if orm_token.user:
|
if orm_token.user:
|
||||||
orm_token.user.last_activity = now
|
orm_token.user.last_activity = now
|
||||||
model = self.user_model(self.users[orm_token.user])
|
model = self.user_model(self.users[orm_token.user])
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import json
|
import json
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import timedelta, timezone
|
||||||
|
|
||||||
from async_generator import aclosing
|
from async_generator import aclosing
|
||||||
from dateutil.parser import parse as parse_date
|
from dateutil.parser import parse as parse_date
|
||||||
@@ -23,6 +23,7 @@ from ..utils import (
|
|||||||
maybe_future,
|
maybe_future,
|
||||||
url_escape_path,
|
url_escape_path,
|
||||||
url_path_join,
|
url_path_join,
|
||||||
|
utcnow,
|
||||||
)
|
)
|
||||||
from .base import APIHandler
|
from .base import APIHandler
|
||||||
|
|
||||||
@@ -367,7 +368,7 @@ class UserTokenListAPIHandler(APIHandler):
|
|||||||
if not user:
|
if not user:
|
||||||
raise web.HTTPError(404, "No such user: %s" % user_name)
|
raise web.HTTPError(404, "No such user: %s" % user_name)
|
||||||
|
|
||||||
now = datetime.utcnow()
|
now = utcnow(with_tz=False)
|
||||||
api_tokens = []
|
api_tokens = []
|
||||||
|
|
||||||
def sort_key(token):
|
def sort_key(token):
|
||||||
@@ -843,7 +844,7 @@ def _parse_timestamp(timestamp):
|
|||||||
# strip timezone info to naive UTC datetime
|
# strip timezone info to naive UTC datetime
|
||||||
dt = dt.astimezone(timezone.utc).replace(tzinfo=None)
|
dt = dt.astimezone(timezone.utc).replace(tzinfo=None)
|
||||||
|
|
||||||
now = datetime.utcnow()
|
now = utcnow(with_tz=False)
|
||||||
if (dt - now) > timedelta(minutes=59):
|
if (dt - now) > timedelta(minutes=59):
|
||||||
raise web.HTTPError(
|
raise web.HTTPError(
|
||||||
400,
|
400,
|
||||||
|
@@ -96,6 +96,7 @@ from .utils import (
|
|||||||
subdomain_hook_idna,
|
subdomain_hook_idna,
|
||||||
subdomain_hook_legacy,
|
subdomain_hook_legacy,
|
||||||
url_path_join,
|
url_path_join,
|
||||||
|
utcnow,
|
||||||
)
|
)
|
||||||
|
|
||||||
common_aliases = {
|
common_aliases = {
|
||||||
@@ -2093,7 +2094,7 @@ class JupyterHub(Application):
|
|||||||
# we don't want to allow user.created to be undefined,
|
# we don't want to allow user.created to be undefined,
|
||||||
# so initialize it to last_activity (if defined) or now.
|
# so initialize it to last_activity (if defined) or now.
|
||||||
if not user.created:
|
if not user.created:
|
||||||
user.created = user.last_activity or datetime.utcnow()
|
user.created = user.last_activity or utcnow(with_tz=False)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
# The allowed_users set and the users in the db are now the same.
|
# The allowed_users set and the users in the db are now the same.
|
||||||
@@ -3273,7 +3274,7 @@ class JupyterHub(Application):
|
|||||||
routes = await self.proxy.get_all_routes()
|
routes = await self.proxy.get_all_routes()
|
||||||
users_count = 0
|
users_count = 0
|
||||||
active_users_count = 0
|
active_users_count = 0
|
||||||
now = datetime.utcnow()
|
now = utcnow(with_tz=False)
|
||||||
for prefix, route in routes.items():
|
for prefix, route in routes.items():
|
||||||
route_data = route['data']
|
route_data = route['data']
|
||||||
if 'user' not in route_data:
|
if 'user' not in route_data:
|
||||||
|
@@ -10,7 +10,7 @@ import re
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
from http.client import responses
|
from http.client import responses
|
||||||
from urllib.parse import parse_qs, parse_qsl, urlencode, urlparse, urlunparse
|
from urllib.parse import parse_qs, parse_qsl, urlencode, urlparse, urlunparse
|
||||||
|
|
||||||
@@ -47,6 +47,7 @@ from ..utils import (
|
|||||||
maybe_future,
|
maybe_future,
|
||||||
url_escape_path,
|
url_escape_path,
|
||||||
url_path_join,
|
url_path_join,
|
||||||
|
utcnow,
|
||||||
)
|
)
|
||||||
|
|
||||||
# pattern for the authentication token header
|
# pattern for the authentication token header
|
||||||
@@ -293,7 +294,7 @@ class BaseHandler(RequestHandler):
|
|||||||
recorded (bool): True if activity was recorded, False if not.
|
recorded (bool): True if activity was recorded, False if not.
|
||||||
"""
|
"""
|
||||||
if timestamp is None:
|
if timestamp is None:
|
||||||
timestamp = datetime.utcnow()
|
timestamp = utcnow(with_tz=False)
|
||||||
resolution = self.settings.get("activity_resolution", 0)
|
resolution = self.settings.get("activity_resolution", 0)
|
||||||
if not obj.last_activity or resolution == 0:
|
if not obj.last_activity or resolution == 0:
|
||||||
self.log.debug("Recording first activity for %s", obj)
|
self.log.debug("Recording first activity for %s", obj)
|
||||||
@@ -381,7 +382,7 @@ class BaseHandler(RequestHandler):
|
|||||||
orm_token = self.get_token()
|
orm_token = self.get_token()
|
||||||
if orm_token is None:
|
if orm_token is None:
|
||||||
return None
|
return None
|
||||||
now = datetime.utcnow()
|
now = utcnow(with_tz=False)
|
||||||
recorded = self._record_activity(orm_token, now)
|
recorded = self._record_activity(orm_token, now)
|
||||||
if orm_token.user:
|
if orm_token.user:
|
||||||
# FIXME: scopes should give us better control than this
|
# FIXME: scopes should give us better control than this
|
||||||
|
@@ -14,7 +14,7 @@ from tornado.httputil import url_concat
|
|||||||
from .. import __version__
|
from .. import __version__
|
||||||
from ..metrics import SERVER_POLL_DURATION_SECONDS, ServerPollStatus
|
from ..metrics import SERVER_POLL_DURATION_SECONDS, ServerPollStatus
|
||||||
from ..scopes import needs_scope
|
from ..scopes import needs_scope
|
||||||
from ..utils import maybe_future, url_escape_path, url_path_join
|
from ..utils import maybe_future, url_escape_path, url_path_join, utcnow
|
||||||
from .base import BaseHandler
|
from .base import BaseHandler
|
||||||
|
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ class TokenPageHandler(BaseHandler):
|
|||||||
def sort_key(token):
|
def sort_key(token):
|
||||||
return (token.last_activity or never, token.created or never)
|
return (token.last_activity or never, token.created or never)
|
||||||
|
|
||||||
now = datetime.utcnow()
|
now = utcnow(with_tz=False)
|
||||||
|
|
||||||
# group oauth client tokens by client id
|
# group oauth client tokens by client id
|
||||||
all_tokens = defaultdict(list)
|
all_tokens = defaultdict(list)
|
||||||
|
@@ -4,7 +4,8 @@
|
|||||||
import enum
|
import enum
|
||||||
import json
|
import json
|
||||||
from base64 import decodebytes, encodebytes
|
from base64 import decodebytes, encodebytes
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
import alembic.command
|
import alembic.command
|
||||||
import alembic.config
|
import alembic.config
|
||||||
@@ -40,10 +41,10 @@ from sqlalchemy.pool import StaticPool
|
|||||||
from sqlalchemy.types import LargeBinary, Text, TypeDecorator
|
from sqlalchemy.types import LargeBinary, Text, TypeDecorator
|
||||||
from tornado.log import app_log
|
from tornado.log import app_log
|
||||||
|
|
||||||
from .utils import compare_token, hash_token, new_token, random_port
|
from .utils import compare_token, hash_token, new_token, random_port, utcnow
|
||||||
|
|
||||||
# top-level variable for easier mocking in tests
|
# top-level variable for easier mocking in tests
|
||||||
utcnow = datetime.utcnow
|
utcnow = partial(utcnow, with_tz=False)
|
||||||
|
|
||||||
|
|
||||||
class JSONDict(TypeDecorator):
|
class JSONDict(TypeDecorator):
|
||||||
@@ -278,7 +279,7 @@ class User(Base):
|
|||||||
return {s.name: s for s in self._orm_spawners}
|
return {s.name: s for s in self._orm_spawners}
|
||||||
|
|
||||||
admin = Column(Boolean(create_constraint=False), default=False)
|
admin = Column(Boolean(create_constraint=False), default=False)
|
||||||
created = Column(DateTime, default=datetime.utcnow)
|
created = Column(DateTime, default=utcnow)
|
||||||
last_activity = Column(DateTime, nullable=True)
|
last_activity = Column(DateTime, nullable=True)
|
||||||
|
|
||||||
api_tokens = relationship(
|
api_tokens = relationship(
|
||||||
@@ -665,8 +666,8 @@ class APIToken(Hashed, Base):
|
|||||||
session_id = Column(Unicode(255), nullable=True)
|
session_id = Column(Unicode(255), nullable=True)
|
||||||
|
|
||||||
# token metadata for bookkeeping
|
# token metadata for bookkeeping
|
||||||
now = datetime.utcnow # for expiry
|
now = utcnow # for expiry
|
||||||
created = Column(DateTime, default=datetime.utcnow)
|
created = Column(DateTime, default=utcnow)
|
||||||
expires_at = Column(DateTime, default=None, nullable=True)
|
expires_at = Column(DateTime, default=None, nullable=True)
|
||||||
last_activity = Column(DateTime)
|
last_activity = Column(DateTime)
|
||||||
note = Column(Unicode(1023))
|
note = Column(Unicode(1023))
|
||||||
@@ -855,7 +856,7 @@ class OAuthCode(Expiring, Base):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def now():
|
def now():
|
||||||
return datetime.utcnow().timestamp()
|
return utcnow(with_tz=True).timestamp()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find(cls, db, code):
|
def find(cls, db, code):
|
||||||
|
@@ -4,11 +4,11 @@ Run with old versions of jupyterhub to test upgrade/downgrade
|
|||||||
|
|
||||||
used in test_db.py
|
used in test_db.py
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import jupyterhub
|
import jupyterhub
|
||||||
from jupyterhub import orm
|
from jupyterhub import orm
|
||||||
|
from jupyterhub.utils import utcnow
|
||||||
|
|
||||||
|
|
||||||
def populate_db(url):
|
def populate_db(url):
|
||||||
@@ -117,10 +117,11 @@ def populate_db(url):
|
|||||||
assert user.created
|
assert user.created
|
||||||
assert admin.created
|
assert admin.created
|
||||||
# set last_activity
|
# set last_activity
|
||||||
user.last_activity = datetime.utcnow()
|
now = utcnow().replace(tzinfo=None)
|
||||||
|
user.last_activity = now
|
||||||
spawner = user.orm_spawners['']
|
spawner = user.orm_spawners['']
|
||||||
spawner.started = datetime.utcnow()
|
spawner.started = now
|
||||||
spawner.last_activity = datetime.utcnow()
|
spawner.last_activity = now
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@@ -2444,7 +2444,7 @@ async def test_update_server_activity(app, user, server_name, fresh):
|
|||||||
# we use naive utc internally
|
# we use naive utc internally
|
||||||
# initialize last_activity for one named and the default server
|
# initialize last_activity for one named and the default server
|
||||||
for name in ("", "exists"):
|
for name in ("", "exists"):
|
||||||
user.spawners[name].orm_spawner.last_activity = now.replace(tzinfo=None)
|
user.spawners[name].orm_spawner.last_activity = internal_now
|
||||||
app.db.commit()
|
app.db.commit()
|
||||||
|
|
||||||
td = timedelta(minutes=1)
|
td = timedelta(minutes=1)
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -11,6 +11,7 @@ import pytest
|
|||||||
from .. import crypto, objects, orm, roles
|
from .. import crypto, objects, orm, roles
|
||||||
from ..emptyclass import EmptyClass
|
from ..emptyclass import EmptyClass
|
||||||
from ..user import User
|
from ..user import User
|
||||||
|
from ..utils import utcnow
|
||||||
from .mocking import MockSpawner
|
from .mocking import MockSpawner
|
||||||
|
|
||||||
|
|
||||||
@@ -121,7 +122,7 @@ def test_token_expiry(db):
|
|||||||
user = orm.User(name='parker')
|
user = orm.User(name='parker')
|
||||||
db.add(user)
|
db.add(user)
|
||||||
db.commit()
|
db.commit()
|
||||||
now = datetime.utcnow()
|
now = utcnow(with_tz=False)
|
||||||
token = user.new_api_token(expires_in=60)
|
token = user.new_api_token(expires_in=60)
|
||||||
orm_token = orm.APIToken.find(db, token=token)
|
orm_token = orm.APIToken.find(db, token=token)
|
||||||
assert orm_token
|
assert orm_token
|
||||||
@@ -506,7 +507,7 @@ def test_expiring_api_token(app, user):
|
|||||||
assert found is orm_token
|
assert found is orm_token
|
||||||
|
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
orm.APIToken, 'now', lambda: datetime.utcnow() + timedelta(seconds=60)
|
orm.APIToken, 'now', lambda: utcnow(with_tz=False) + timedelta(seconds=60)
|
||||||
):
|
):
|
||||||
found = orm.APIToken.find(db, token)
|
found = orm.APIToken.find(db, token)
|
||||||
assert found is None
|
assert found is None
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
import json
|
import json
|
||||||
import warnings
|
import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
from urllib.parse import quote, urlparse
|
from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
@@ -25,6 +25,7 @@ from .utils import (
|
|||||||
subdomain_hook_legacy,
|
subdomain_hook_legacy,
|
||||||
url_escape_path,
|
url_escape_path,
|
||||||
url_path_join,
|
url_path_join,
|
||||||
|
utcnow,
|
||||||
)
|
)
|
||||||
|
|
||||||
# detailed messages about the most common failure-to-start errors,
|
# detailed messages about the most common failure-to-start errors,
|
||||||
@@ -757,7 +758,7 @@ class User:
|
|||||||
# update spawner start time, and activity for both spawner and user
|
# update spawner start time, and activity for both spawner and user
|
||||||
self.last_activity = (
|
self.last_activity = (
|
||||||
spawner.orm_spawner.started
|
spawner.orm_spawner.started
|
||||||
) = spawner.orm_spawner.last_activity = datetime.utcnow()
|
) = spawner.orm_spawner.last_activity = utcnow(with_tz=False)
|
||||||
db.commit()
|
db.commit()
|
||||||
# wait for spawner.start to return
|
# wait for spawner.start to return
|
||||||
# run optional preparation work to bootstrap the notebook
|
# run optional preparation work to bootstrap the notebook
|
||||||
@@ -964,7 +965,9 @@ class User:
|
|||||||
status = await spawner.poll()
|
status = await spawner.poll()
|
||||||
if status is None:
|
if status is None:
|
||||||
await spawner.stop()
|
await spawner.stop()
|
||||||
self.last_activity = spawner.orm_spawner.last_activity = datetime.utcnow()
|
self.last_activity = spawner.orm_spawner.last_activity = utcnow(
|
||||||
|
with_tz=False
|
||||||
|
)
|
||||||
# remove server entry from db
|
# remove server entry from db
|
||||||
spawner.server = None
|
spawner.server = None
|
||||||
if not spawner.will_resume:
|
if not spawner.will_resume:
|
||||||
|
@@ -654,9 +654,18 @@ async def iterate_until(deadline_future, generator):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
||||||
def utcnow():
|
def utcnow(*, with_tz=True):
|
||||||
"""Return timezone-aware utcnow"""
|
"""Return utcnow
|
||||||
return datetime.now(timezone.utc)
|
|
||||||
|
with_tz (default): returns tz-aware datetime in UTC
|
||||||
|
|
||||||
|
if with_tz=False, returns UTC timestamp without tzinfo
|
||||||
|
(used for most internal timestamp storage because databases often don't preserve tz info)
|
||||||
|
"""
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
if not with_tz:
|
||||||
|
now = now.replace(tzinfo=None)
|
||||||
|
return now
|
||||||
|
|
||||||
|
|
||||||
def _parse_accept_header(accept):
|
def _parse_accept_header(accept):
|
||||||
|
Reference in New Issue
Block a user