Django 4.2 upgrade

This commit is contained in:
Sonny Bakker 2024-09-07 20:50:38 +02:00
parent b78f03d3b0
commit dfb049ae14
32 changed files with 345 additions and 414 deletions

View file

@ -5,23 +5,22 @@ authors = [{name = 'Sonny', email= 'sonnyba871@gmail.com'}]
license = {text = 'GPL-3.0'} license = {text = 'GPL-3.0'}
requires-python = '>=3.11' requires-python = '>=3.11'
dependencies = [ dependencies = [
'django~=3.2', 'django~=4.2',
'celery~=5.0', 'celery~=5.4',
'psycopg2', 'psycopg',
'django-axes', 'django-axes',
'django-celery-beat~=2.5.0', 'django-celery-beat~=2.7.0',
'django-registration-redux~=2.7', 'django-registration-redux~=2.7',
'django-rest-framework', 'django-rest-framework',
"python-memcached<=1.59", 'pymemcache',
'python-dotenv~=0.12', 'python-dotenv~=1.0.1',
'ftfy~=5.8', 'ftfy~=6.2',
'requests', 'requests',
'requests_oauthlib', 'requests_oauthlib',
'feedparser', 'feedparser',
'bleach', 'bleach',
'beautifulsoup4', 'beautifulsoup4',
'lxml', 'lxml',
"setuptools>=74.0.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]
@ -33,10 +32,11 @@ testing = [
] ]
development = [ development = [
'django-debug-toolbar', 'django-debug-toolbar',
'django-stubs',
'django-extensions', 'django-extensions',
] ]
ci = ['coverage>=5.3.1'] ci = ['coverage~=7.6.1']
production = ['gunicorn~=20.0', 'sentry-sdk~=1.0'] production = ['gunicorn~=23.0', 'sentry-sdk~=2.0']
[tool.uv] [tool.uv]
environments = ["sys_platform == 'linux'"] environments = ["sys_platform == 'linux'"]

View file

@ -2,7 +2,7 @@ from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.contrib.auth.forms import UserChangeForm from django.contrib.auth.forms import UserChangeForm
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from newsreader.accounts.models import User from newsreader.accounts.models import User

View file

@ -106,11 +106,11 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache", "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211", "LOCATION": "memcached:11211",
}, },
"axes": { "axes": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache", "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211", "LOCATION": "memcached:11211",
}, },
} }

View file

@ -22,11 +22,11 @@ AXES_ENABLED = False
CACHES = { CACHES = {
"default": { "default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache", "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211", "LOCATION": "memcached:11211",
}, },
"axes": { "axes": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache", "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "memcached:11211", "LOCATION": "memcached:11211",
}, },
} }

View file

@ -23,7 +23,6 @@ class App extends React.Component {
<PostList <PostList
feedUrl={this.props.feedUrl} feedUrl={this.props.feedUrl}
subredditUrl={this.props.subredditUrl} subredditUrl={this.props.subredditUrl}
timelineUrl={this.props.timelineUrl}
timezone={this.props.timezone} timezone={this.props.timezone}
/> />
@ -35,7 +34,6 @@ class App extends React.Component {
selectedType={this.props.selectedType} selectedType={this.props.selectedType}
feedUrl={this.props.feedUrl} feedUrl={this.props.feedUrl}
subredditUrl={this.props.subredditUrl} subredditUrl={this.props.subredditUrl}
timelineUrl={this.props.timelineUrl}
categoriesUrl={this.props.categoriesUrl} categoriesUrl={this.props.categoriesUrl}
timezone={this.props.timezone} timezone={this.props.timezone}
autoMarking={this.props.autoMarking} autoMarking={this.props.autoMarking}

View file

@ -3,14 +3,7 @@ import { connect } from 'react-redux';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js'; import { unSelectPost, markPostRead, toggleSaved } from '../actions/posts.js';
import { import { SAVED_TYPE, SUBREDDIT } from '../constants.js';
CATEGORY_TYPE,
RULE_TYPE,
SAVED_TYPE,
FEED,
SUBREDDIT,
TWITTER_TIMELINE,
} from '../constants.js';
import { formatDatetime } from '../../../utils.js'; import { formatDatetime } from '../../../utils.js';
class PostModal extends React.Component { class PostModal extends React.Component {
@ -62,9 +55,6 @@ class PostModal extends React.Component {
case SUBREDDIT: case SUBREDDIT:
ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`; ruleUrl = `${this.props.subredditUrl}/${this.props.rule.id}/`;
break; break;
case TWITTER_TIMELINE:
ruleUrl = '#';
break;
default: default:
ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`; ruleUrl = `${this.props.feedUrl}/${this.props.rule.id}/`;
break; break;

View file

@ -4,11 +4,8 @@ import Cookies from 'js-cookie';
import { import {
CATEGORY_TYPE, CATEGORY_TYPE,
RULE_TYPE,
SAVED_TYPE, SAVED_TYPE,
FEED,
SUBREDDIT, SUBREDDIT,
TWITTER_TIMELINE,
} from '../../constants.js'; } from '../../constants.js';
import { selectPost, toggleSaved } from '../../actions/posts.js'; import { selectPost, toggleSaved } from '../../actions/posts.js';
import { formatDatetime } from '../../../../utils.js'; import { formatDatetime } from '../../../../utils.js';
@ -28,8 +25,6 @@ class PostItem extends React.Component {
let ruleUrl = ''; let ruleUrl = '';
if (rule.type === SUBREDDIT) { if (rule.type === SUBREDDIT) {
ruleUrl = `${this.props.subredditUrl}/${rule.id}/`; ruleUrl = `${this.props.subredditUrl}/${rule.id}/`;
} else if (rule.type === TWITTER_TIMELINE) {
ruleUrl = `${this.props.timelineUrl}/${rule.id}/`;
} else { } else {
ruleUrl = `${this.props.feedUrl}/${rule.id}/`; ruleUrl = `${this.props.feedUrl}/${rule.id}/`;
} }

View file

@ -65,7 +65,6 @@ class PostList extends React.Component {
selected: this.props.selected, selected: this.props.selected,
feedUrl: this.props.feedUrl, feedUrl: this.props.feedUrl,
subredditUrl: this.props.subredditUrl, subredditUrl: this.props.subredditUrl,
timelineUrl: this.props.timelineUrl,
timezone: this.props.timezone, timezone: this.props.timezone,
}; };

View file

@ -4,4 +4,3 @@ export const SAVED_TYPE = 'SAVED';
export const SUBREDDIT = 'subreddit'; export const SUBREDDIT = 'subreddit';
export const FEED = 'feed'; export const FEED = 'feed';
export const TWITTER_TIMELINE = 'twitter_timeline';

View file

@ -12,14 +12,13 @@ if (page) {
const store = configureStore(); const store = configureStore();
const settings = JSON.parse(document.getElementById('homepageSettings').textContent); const settings = JSON.parse(document.getElementById('homepageSettings').textContent);
const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings; const { feedUrl, subredditUrl, categoriesUrl } = settings;
const app = ( const app = (
<Provider store={store}> <Provider store={store}>
<App <App
feedUrl={feedUrl.substring(1, feedUrl.length - 3)} feedUrl={feedUrl.substring(1, feedUrl.length - 3)}
subredditUrl={subredditUrl.substring(1, subredditUrl.length - 3)} subredditUrl={subredditUrl.substring(1, subredditUrl.length - 3)}
timelineUrl={timelineUrl.substring(1, timelineUrl.length - 3)}
categoriesUrl={categoriesUrl.substring(1, categoriesUrl.length - 3)} categoriesUrl={categoriesUrl.substring(1, categoriesUrl.length - 3)}
timezone={settings.timezone} timezone={settings.timezone}
autoMarking={settings.autoMarking} autoMarking={settings.autoMarking}

View file

@ -6,8 +6,6 @@ from datetime import timedelta
from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist
from django.utils import timezone from django.utils import timezone
import pytz
from feedparser import parse from feedparser import parse
from newsreader.news.collection.base import ( from newsreader.news.collection.base import (
@ -58,7 +56,6 @@ class FeedBuilder(PostBuilder):
"published_parsed": "publication_date", "published_parsed": "publication_date",
"author": "author", "author": "author",
} }
tz = pytz.timezone(self.stream.rule.timezone)
data = {"rule_id": self.stream.rule.pk} data = {"rule_id": self.stream.rule.pk}
for field, model_field in field_mapping.items(): for field, model_field in field_mapping.items():
@ -68,7 +65,7 @@ class FeedBuilder(PostBuilder):
value = truncate_text(Post, model_field, entry[field]) value = truncate_text(Post, model_field, entry[field])
if field == "published_parsed": if field == "published_parsed":
data[model_field] = build_publication_date(value, tz) data[model_field] = build_publication_date(value)
elif field == "summary": elif field == "summary":
data[model_field] = self.sanitize_fragment(value) data[model_field] = self.sanitize_fragment(value)
else: else:

View file

@ -1,24 +1,15 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import pytz
from newsreader.core.forms import CheckboxInput from newsreader.core.forms import CheckboxInput
from newsreader.news.collection.forms.base import CollectionRuleForm from newsreader.news.collection.forms.base import CollectionRuleForm
from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.models import CollectionRule
class FeedForm(CollectionRuleForm): class FeedForm(CollectionRuleForm):
timezone = forms.ChoiceField(
widget=forms.Select(attrs={"size": len(pytz.all_timezones)}),
choices=((timezone, timezone) for timezone in pytz.all_timezones),
help_text=_("The timezone which the feed uses"),
initial=pytz.utc,
)
class Meta: class Meta:
model = CollectionRule model = CollectionRule
fields = ("name", "url", "timezone", "favicon", "category") fields = ("name", "url", "favicon", "category")
class OPMLImportForm(forms.Form): class OPMLImportForm(forms.Form):

View file

@ -3,8 +3,6 @@ from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import pytz
from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.forms.base import CollectionRuleForm from newsreader.news.collection.forms.base import CollectionRuleForm
from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.models import CollectionRule
@ -36,7 +34,6 @@ class SubRedditForm(CollectionRuleForm):
instance = super().save(commit=False) instance = super().save(commit=False)
instance.type = RuleTypeChoices.subreddit instance.type = RuleTypeChoices.subreddit
instance.timezone = str(pytz.utc)
if commit: if commit:
instance.save() instance.save()

View file

@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-09-07 17:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('collection', '0016_alter_collectionrule_timezone'),
]
operations = [
migrations.RemoveField(
model_name='collectionrule',
name='timezone',
),
]

View file

@ -3,8 +3,6 @@ from django.conf import settings
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
import pytz
from newsreader.core.models import TimeStampedModel from newsreader.core.models import TimeStampedModel
from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.choices import RuleTypeChoices
@ -26,12 +24,6 @@ class CollectionRule(TimeStampedModel):
) )
favicon = models.URLField(blank=True, null=True) favicon = models.URLField(blank=True, null=True)
timezone = models.CharField(
choices=((timezone, timezone) for timezone in pytz.all_timezones),
max_length=100,
default=str(pytz.utc),
)
category = models.ForeignKey( category = models.ForeignKey(
"core.Category", "core.Category",
blank=True, blank=True,

View file

@ -1,7 +1,7 @@
import logging import logging
from concurrent.futures import ThreadPoolExecutor, as_completed from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta from datetime import datetime, timedelta, timezone
from html import unescape from html import unescape
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
from urllib.parse import urlencode from urllib.parse import urlencode
@ -9,10 +9,8 @@ from uuid import uuid4
from django.conf import settings from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.utils import timezone
from django.utils.html import format_html from django.utils.html import format_html
import pytz
import requests import requests
from newsreader.news.collection.base import ( from newsreader.news.collection.base import (
@ -214,8 +212,8 @@ class RedditBuilder(PostBuilder):
body = self.get_url_post(title, direct_url) body = self.get_url_post(title, direct_url)
try: try:
parsed_date = datetime.fromtimestamp(entry_data["created_utc"]) _created_date = datetime.fromtimestamp(entry_data["created_utc"])
created_date = pytz.utc.localize(parsed_date) created_date = _created_date.replace(tzinfo=timezone.utc)
except (OverflowError, OSError) as e: except (OverflowError, OSError) as e:
raise BuilderParseException(payload=entry) from e raise BuilderParseException(payload=entry) from e
except KeyError as e: except KeyError as e:
@ -370,7 +368,7 @@ class RedditClient(PostClient):
continue continue
finally: finally:
stream.rule.last_run = timezone.now() stream.rule.last_run = datetime.now(tz=timezone.utc)
stream.rule.save() stream.rule.save()

View file

@ -1,7 +1,7 @@
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import send_mail from django.core.mail import send_mail
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
import requests import requests

View file

@ -1,12 +1,10 @@
import json import json
from datetime import date, datetime, time from datetime import date, datetime, time, timezone
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
@ -153,22 +151,22 @@ class NestedRuleListViewTestCase(TestCase):
FeedPostFactory( FeedPostFactory(
title="I'm the first post", title="I'm the first post",
rule=rule, rule=rule,
publication_date=datetime.combine( publication_date=(
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc)
), )
), ),
FeedPostFactory( FeedPostFactory(
title="I'm the second post", title="I'm the second post",
rule=rule, rule=rule,
publication_date=datetime.combine( publication_date=(
date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc)
), ),
), ),
FeedPostFactory( FeedPostFactory(
title="I'm the third post", title="I'm the third post",
rule=rule, rule=rule,
publication_date=datetime.combine( publication_date=(
date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc)
), ),
), ),
] ]

View file

@ -1,10 +1,7 @@
from datetime import datetime from datetime import datetime, timezone
from unittest.mock import Mock from unittest.mock import Mock
from django.test import TestCase from django.test import TestCase
from django.utils import timezone
import pytz
from freezegun import freeze_time from freezegun import freeze_time
@ -51,7 +48,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0] post = posts[0]
publication_date = datetime( publication_date = datetime(
2019, 5, 20, hour=16, minute=32, second=38, tzinfo=pytz.utc 2019, 5, 20, hour=16, minute=32, second=38, tzinfo=timezone.utc
) )
self.assertEqual( self.assertEqual(
@ -75,7 +72,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1] post = posts[1]
publication_date = datetime( publication_date = datetime(
2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc
) )
self.assertEqual( self.assertEqual(
@ -110,7 +107,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0] post = posts[0]
publication_date = datetime( publication_date = datetime(
2019, 5, 20, hour=16, minute=7, second=37, tzinfo=pytz.utc 2019, 5, 20, hour=16, minute=7, second=37, tzinfo=timezone.utc
) )
self.assertEqual(post.publication_date, publication_date) self.assertEqual(post.publication_date, publication_date)
@ -125,7 +122,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1] post = posts[1]
publication_date = datetime( publication_date = datetime(
2019, 5, 20, hour=12, minute=19, second=19, tzinfo=pytz.utc 2019, 5, 20, hour=12, minute=19, second=19, tzinfo=timezone.utc
) )
self.assertEqual(post.publication_date, publication_date) self.assertEqual(post.publication_date, publication_date)
@ -149,7 +146,7 @@ class FeedBuilderTestCase(TestCase):
self.assertEqual( self.assertEqual(
post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30" post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30"
) )
self.assertEqual(post.created, timezone.now()) self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual( self.assertEqual(
post.remote_identifier, post.remote_identifier,
"https://www.bbc.co.uk/news/world-us-canada-48338168", "https://www.bbc.co.uk/news/world-us-canada-48338168",
@ -160,7 +157,7 @@ class FeedBuilderTestCase(TestCase):
self.assertEqual( self.assertEqual(
post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30" post.publication_date.strftime("%Y-%m-%d %H:%M"), "2019-10-30 12:30"
) )
self.assertEqual(post.created, timezone.now()) self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual( self.assertEqual(
post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
) )
@ -178,7 +175,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0] post = posts[0]
self.assertEqual(post.created, timezone.now()) self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual( self.assertEqual(
post.remote_identifier, post.remote_identifier,
"https://www.bbc.co.uk/news/world-us-canada-48338168", "https://www.bbc.co.uk/news/world-us-canada-48338168",
@ -186,7 +183,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1] post = posts[1]
self.assertEqual(post.created, timezone.now()) self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual( self.assertEqual(
post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
) )
@ -238,7 +235,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[0] post = posts[0]
self.assertEqual(post.created, timezone.now()) self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual( self.assertEqual(
post.remote_identifier, post.remote_identifier,
"https://www.bbc.co.uk/news/world-us-canada-48338168", "https://www.bbc.co.uk/news/world-us-canada-48338168",
@ -247,7 +244,7 @@ class FeedBuilderTestCase(TestCase):
post = posts[1] post = posts[1]
self.assertEqual(post.created, timezone.now()) self.assertEqual(post.created, datetime.now(tz=timezone.utc))
self.assertEqual( self.assertEqual(
post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739" post.remote_identifier, "https://www.bbc.co.uk/news/technology-48334739"
) )

View file

@ -1,11 +1,8 @@
from datetime import date, datetime, time from datetime import datetime, timezone
from time import struct_time from time import struct_time
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from django.test import TestCase from django.test import TestCase
from django.utils import timezone
import pytz
from freezegun import freeze_time from freezegun import freeze_time
@ -47,10 +44,10 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 3) self.assertEqual(Post.objects.count(), 3)
self.assertEquals(rule.succeeded, True) self.assertEqual(rule.succeeded, True)
self.assertEquals(rule.last_run, timezone.now()) self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
self.assertEquals(rule.error, None) self.assertEqual(rule.error, None)
def test_emtpy_batch(self): def test_emtpy_batch(self):
self.mocked_fetch.return_value = Mock() self.mocked_fetch.return_value = Mock()
@ -63,10 +60,10 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, True) self.assertEqual(rule.succeeded, True)
self.assertEquals(rule.error, None) self.assertEqual(rule.error, None)
self.assertEquals(rule.last_run, timezone.now()) self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
def test_not_found(self): def test_not_found(self):
self.mocked_fetch.side_effect = StreamNotFoundException self.mocked_fetch.side_effect = StreamNotFoundException
@ -77,14 +74,14 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream not found") self.assertEqual(rule.error, "Stream not found")
def test_denied(self): def test_denied(self):
self.mocked_fetch.side_effect = StreamDeniedException self.mocked_fetch.side_effect = StreamDeniedException
old_run = timezone.make_aware(datetime(2019, 10, 30, 12, 30)) old_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
rule = FeedFactory(last_run=old_run) rule = FeedFactory(last_run=old_run)
collector = FeedCollector() collector = FeedCollector()
@ -92,15 +89,15 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream does not have sufficient permissions") self.assertEqual(rule.error, "Stream does not have sufficient permissions")
self.assertEquals(rule.last_run, timezone.now()) self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
def test_forbidden(self): def test_forbidden(self):
self.mocked_fetch.side_effect = StreamForbiddenException self.mocked_fetch.side_effect = StreamForbiddenException
old_run = pytz.utc.localize(datetime(2019, 10, 30, 12, 30)) old_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
rule = FeedFactory(last_run=old_run) rule = FeedFactory(last_run=old_run)
collector = FeedCollector() collector = FeedCollector()
@ -108,17 +105,15 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream forbidden") self.assertEqual(rule.error, "Stream forbidden")
self.assertEquals(rule.last_run, timezone.now()) self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
def test_timed_out(self): def test_timed_out(self):
self.mocked_fetch.side_effect = StreamTimeOutException self.mocked_fetch.side_effect = StreamTimeOutException
last_run = timezone.make_aware( last_run = datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
datetime.combine(date=date(2019, 10, 30), time=time(12, 30))
)
rule = FeedFactory(last_run=last_run) rule = FeedFactory(last_run=last_run)
collector = FeedCollector() collector = FeedCollector()
@ -126,11 +121,11 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream timed out") self.assertEqual(rule.error, "Stream timed out")
self.assertEquals( self.assertEqual(
rule.last_run, pytz.utc.localize(datetime(2019, 10, 30, 12, 30)) rule.last_run, datetime(2019, 10, 30, 12, 30, tzinfo=timezone.utc)
) )
def test_duplicates(self): def test_duplicates(self):
@ -139,7 +134,7 @@ class FeedCollectorTestCase(TestCase):
rule = FeedFactory() rule = FeedFactory()
aware_datetime = build_publication_date( aware_datetime = build_publication_date(
struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), pytz.utc struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0))
) )
FeedPostFactory( FeedPostFactory(
@ -152,7 +147,7 @@ class FeedCollectorTestCase(TestCase):
) )
aware_datetime = build_publication_date( aware_datetime = build_publication_date(
struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), pytz.utc struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0))
) )
FeedPostFactory( FeedPostFactory(
@ -165,7 +160,7 @@ class FeedCollectorTestCase(TestCase):
) )
aware_datetime = build_publication_date( aware_datetime = build_publication_date(
struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), pytz.utc struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0))
) )
FeedPostFactory( FeedPostFactory(
@ -183,10 +178,10 @@ class FeedCollectorTestCase(TestCase):
rule.refresh_from_db() rule.refresh_from_db()
self.assertEquals(Post.objects.count(), 3) self.assertEqual(Post.objects.count(), 3)
self.assertEquals(rule.succeeded, True) self.assertEqual(rule.succeeded, True)
self.assertEquals(rule.last_run, timezone.now()) self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
self.assertEquals(rule.error, None) self.assertEqual(rule.error, None)
def test_items_with_identifiers_get_updated(self): def test_items_with_identifiers_get_updated(self):
self.mocked_parse.return_value = multiple_update_mock self.mocked_parse.return_value = multiple_update_mock
@ -197,7 +192,7 @@ class FeedCollectorTestCase(TestCase):
url="https://www.bbc.co.uk/", url="https://www.bbc.co.uk/",
title="Trump", title="Trump",
body="Foreign Minister Mohammad Javad Zarif", body="Foreign Minister Mohammad Javad Zarif",
publication_date=timezone.now(), publication_date=datetime.now(tz=timezone.utc),
rule=rule, rule=rule,
) )
@ -206,7 +201,7 @@ class FeedCollectorTestCase(TestCase):
url="https://www.bbc.co.uk/", url="https://www.bbc.co.uk/",
title="Huawei's Android loss: How it affects you", title="Huawei's Android loss: How it affects you",
body="Google's move to end business ties with Huawei will", body="Google's move to end business ties with Huawei will",
publication_date=timezone.now(), publication_date=datetime.now(tz=timezone.utc),
rule=rule, rule=rule,
) )
@ -215,7 +210,7 @@ class FeedCollectorTestCase(TestCase):
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080", url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons", title="Birmingham head teacher threatened over LGBT lessons",
body="Police are investigating the messages while an MP", body="Police are investigating the messages while an MP",
publication_date=timezone.now(), publication_date=datetime.now(tz=timezone.utc),
rule=rule, rule=rule,
) )
@ -227,19 +222,19 @@ class FeedCollectorTestCase(TestCase):
second_post.refresh_from_db() second_post.refresh_from_db()
third_post.refresh_from_db() third_post.refresh_from_db()
self.assertEquals(Post.objects.count(), 3) self.assertEqual(Post.objects.count(), 3)
self.assertEquals(rule.succeeded, True) self.assertEqual(rule.succeeded, True)
self.assertEquals(rule.last_run, timezone.now()) self.assertEqual(rule.last_run, datetime.now(tz=timezone.utc))
self.assertEquals(rule.error, None) self.assertEqual(rule.error, None)
self.assertEquals( self.assertEqual(
first_post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif" first_post.title, "Trump's 'genocidal taunts' will not end Iran - Zarif"
) )
self.assertEquals( self.assertEqual(
second_post.title, "Huawei's Android loss: How it affects you" second_post.title, "Huawei's Android loss: How it affects you"
) )
self.assertEquals( self.assertEqual(
third_post.title, "Birmingham head teacher threatened over LGBT lessons" third_post.title, "Birmingham head teacher threatened over LGBT lessons"
) )

View file

@ -1,10 +1,8 @@
from datetime import datetime from datetime import datetime, timezone
from unittest.mock import Mock from unittest.mock import Mock
from django.test import TestCase from django.test import TestCase
import pytz
from newsreader.news.collection.reddit import RedditBuilder from newsreader.news.collection.reddit import RedditBuilder
from newsreader.news.collection.tests.factories import SubredditFactory from newsreader.news.collection.tests.factories import SubredditFactory
from newsreader.news.collection.tests.reddit.builder.mocks import ( from newsreader.news.collection.tests.reddit.builder.mocks import (
@ -53,8 +51,8 @@ class RedditBuilderTestCase(TestCase):
post = posts["hm0qct"] post = posts["hm0qct"]
self.assertEquals(post.rule, subreddit) self.assertEqual(post.rule, subreddit)
self.assertEquals( self.assertEqual(
post.title, post.title,
"Linux Experiences/Rants or Education/Certifications thread - July 06, 2020", "Linux Experiences/Rants or Education/Certifications thread - July 06, 2020",
) )
@ -72,13 +70,13 @@ class RedditBuilderTestCase(TestCase):
post.body, post.body,
) )
self.assertEquals(post.author, "AutoModerator") self.assertEqual(post.author, "AutoModerator")
self.assertEquals( self.assertEqual(
post.url, post.url,
"https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/", "https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/",
) )
self.assertEquals( self.assertEqual(
post.publication_date, pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)) post.publication_date, datetime(2020, 7, 6, 6, 11, 22, tzinfo=timezone.utc)
) )
def test_empty_data(self): def test_empty_data(self):
@ -91,7 +89,7 @@ class RedditBuilderTestCase(TestCase):
builder.build() builder.build()
builder.save() builder.save()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
def test_unknown_mock(self): def test_unknown_mock(self):
builder = RedditBuilder builder = RedditBuilder
@ -103,7 +101,7 @@ class RedditBuilderTestCase(TestCase):
builder.build() builder.build()
builder.save() builder.save()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
def test_html_sanitizing(self): def test_html_sanitizing(self):
builder = RedditBuilder builder = RedditBuilder
@ -121,7 +119,7 @@ class RedditBuilderTestCase(TestCase):
post = posts["hnd7cy"] post = posts["hnd7cy"]
self.assertEquals(post.body, "<article></article>") self.assertEqual(post.body, "<article></article>")
def test_long_author_text_is_truncated(self): def test_long_author_text_is_truncated(self):
builder = RedditBuilder builder = RedditBuilder
@ -139,7 +137,7 @@ class RedditBuilderTestCase(TestCase):
post = posts["hnd7cy"] post = posts["hnd7cy"]
self.assertEquals(post.author, "TheQuantumZeroTheQuantumZeroTheQuantumZ…") self.assertEqual(post.author, "TheQuantumZeroTheQuantumZeroTheQuantumZ…")
def test_long_title_text_is_truncated(self): def test_long_title_text_is_truncated(self):
builder = RedditBuilder builder = RedditBuilder
@ -157,7 +155,7 @@ class RedditBuilderTestCase(TestCase):
post = posts["hnd7cy"] post = posts["hnd7cy"]
self.assertEquals( self.assertEqual(
post.title, post.title,
'Board statement on the LibreOffice 7.0 RC "Personal EditionBoard statement on the LibreOffice 7.0 RC "Personal Edition" label" labelBoard statement on the LibreOffice 7.0 RC "PersBoard statement on t…', 'Board statement on the LibreOffice 7.0 RC "Personal EditionBoard statement on the LibreOffice 7.0 RC "Personal Edition" label" labelBoard statement on the LibreOffice 7.0 RC "PersBoard statement on t…',
) )
@ -174,7 +172,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 2) self.assertEqual(Post.objects.count(), 2)
self.assertCountEqual(("hm0qct", "hna75r"), posts.keys()) self.assertCountEqual(("hm0qct", "hna75r"), posts.keys())
def test_duplicate_in_database(self): def test_duplicate_in_database(self):
@ -191,7 +189,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 5) self.assertEqual(Post.objects.count(), 5)
self.assertCountEqual( self.assertCountEqual(
("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys() ("hm0qct", "hna75r", "hngs71", "hngsj8", "hnd7cy"), posts.keys()
) )
@ -220,11 +218,11 @@ class RedditBuilderTestCase(TestCase):
) )
url = "https://i.redd.it/cm2qybia1va51.jpg" url = "https://i.redd.it/cm2qybia1va51.jpg"
self.assertEquals( self.assertEqual(
"https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/", "https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/",
post.url, post.url,
) )
self.assertEquals( self.assertEqual(
f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
) )
@ -247,11 +245,11 @@ class RedditBuilderTestCase(TestCase):
url = "http://gfycat.com/thatalivedogwoodclubgall" url = "http://gfycat.com/thatalivedogwoodclubgall"
title = "Excited cows have a new brush!" title = "Excited cows have a new brush!"
self.assertEquals( self.assertEqual(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>", f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body, post.body,
) )
self.assertEquals( self.assertEqual(
"https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/", "https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/",
post.url, post.url,
) )
@ -261,10 +259,10 @@ class RedditBuilderTestCase(TestCase):
url = "https://i.imgur.com/usfMVUJ.jpg" url = "https://i.imgur.com/usfMVUJ.jpg"
title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallass cat kittens" title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallass cat kittens"
self.assertEquals( self.assertEqual(
f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
) )
self.assertEquals( self.assertEqual(
"https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/", "https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/",
post.url, post.url,
) )
@ -287,11 +285,11 @@ class RedditBuilderTestCase(TestCase):
url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback" url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback"
self.assertEquals( self.assertEqual(
post.url, post.url,
"https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/", "https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/",
) )
self.assertEquals( self.assertEqual(
f"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>", f"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>",
post.body, post.body,
) )
@ -308,9 +306,9 @@ class RedditBuilderTestCase(TestCase):
post = Post.objects.get() post = Post.objects.get()
self.assertEquals(post.remote_identifier, "hulh8k") self.assertEqual(post.remote_identifier, "hulh8k")
self.assertEquals( self.assertEqual(
post.url, post.url,
"https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/", "https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/",
) )
@ -318,7 +316,7 @@ class RedditBuilderTestCase(TestCase):
title = "Dog splashing in water" title = "Dog splashing in water"
url = "https://gfycat.com/excellentinfantileamericanwigeon" url = "https://gfycat.com/excellentinfantileamericanwigeon"
self.assertEquals( self.assertEqual(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>", f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body, post.body,
) )
@ -335,13 +333,13 @@ class RedditBuilderTestCase(TestCase):
post = Post.objects.get() post = Post.objects.get()
self.assertEquals(post.remote_identifier, "humdlf") self.assertEqual(post.remote_identifier, "humdlf")
self.assertEquals( self.assertEqual(
post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/" post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/"
) )
self.assertEquals( self.assertEqual(
"<div><video controls muted><source src='https://i.imgur.com/grVh2AG.mp4' type='video/mp4' /></video></div>", "<div><video controls muted><source src='https://i.imgur.com/grVh2AG.mp4' type='video/mp4' /></video></div>",
post.body, post.body,
) )
@ -366,7 +364,7 @@ class RedditBuilderTestCase(TestCase):
post.body, post.body,
) )
self.assertEquals( self.assertEqual(
post.url, post.url,
"https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/", "https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/",
) )
@ -381,7 +379,7 @@ class RedditBuilderTestCase(TestCase):
builder.build() builder.build()
builder.save() builder.save()
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
def test_nsfw_not_allowed(self): def test_nsfw_not_allowed(self):
builder = RedditBuilder builder = RedditBuilder
@ -395,7 +393,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hna75r",), posts.keys()) self.assertCountEqual(("hna75r",), posts.keys())
def test_spoiler_not_allowed(self): def test_spoiler_not_allowed(self):
@ -410,7 +408,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hm0qct",), posts.keys()) self.assertCountEqual(("hm0qct",), posts.keys())
def test_already_seen_not_allowed(self): def test_already_seen_not_allowed(self):
@ -425,7 +423,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hna75r",), posts.keys()) self.assertCountEqual(("hna75r",), posts.keys())
def test_upvote_minimum(self): def test_upvote_minimum(self):
@ -440,7 +438,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hna75r",), posts.keys()) self.assertCountEqual(("hna75r",), posts.keys())
def test_comments_minimum(self): def test_comments_minimum(self):
@ -455,7 +453,7 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hm0qct",), posts.keys()) self.assertCountEqual(("hm0qct",), posts.keys())
def test_downvote_maximum(self): def test_downvote_maximum(self):
@ -470,5 +468,5 @@ class RedditBuilderTestCase(TestCase):
posts = {post.remote_identifier: post for post in Post.objects.all()} posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertEquals(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertCountEqual(("hm0qct",), posts.keys()) self.assertCountEqual(("hm0qct",), posts.keys())

View file

@ -1,11 +1,8 @@
from datetime import datetime from datetime import datetime, timezone
from unittest.mock import patch from unittest.mock import patch
from uuid import uuid4 from uuid import uuid4
from django.test import TestCase from django.test import TestCase
from django.utils import timezone
import pytz
from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.exceptions import ( from newsreader.news.collection.exceptions import (
@ -73,23 +70,23 @@ class RedditCollectorTestCase(TestCase):
for subreddit in rules: for subreddit in rules:
with self.subTest(subreddit=subreddit): with self.subTest(subreddit=subreddit):
self.assertEquals(subreddit.succeeded, True) self.assertEqual(subreddit.succeeded, True)
self.assertEquals(subreddit.last_run, timezone.now()) self.assertEqual(subreddit.last_run, datetime.now(tz=timezone.utc))
self.assertEquals(subreddit.error, None) self.assertEqual(subreddit.error, None)
post = Post.objects.get( post = Post.objects.get(
remote_identifier="hph00n", rule__type=RuleTypeChoices.subreddit remote_identifier="hph00n", rule__type=RuleTypeChoices.subreddit
) )
self.assertEquals( self.assertEqual(
post.publication_date, pytz.utc.localize(datetime(2020, 7, 11, 22, 23, 24)) post.publication_date, datetime(2020, 7, 11, 22, 23, 24, tzinfo=timezone.utc)
) )
self.assertEquals(post.author, "HannahB888") self.assertEqual(post.author, "HannahB888")
self.assertEquals( self.assertEqual(
post.title, "Drake Interplanetary Smartkey thing that I made!" post.title, "Drake Interplanetary Smartkey thing that I made!"
) )
self.assertEquals( self.assertEqual(
post.url, post.url,
"https://www.reddit.com/r/starcitizen/comments/hph00n/drake_interplanetary_smartkey_thing_that_i_made/", "https://www.reddit.com/r/starcitizen/comments/hph00n/drake_interplanetary_smartkey_thing_that_i_made/",
) )
@ -98,16 +95,16 @@ class RedditCollectorTestCase(TestCase):
remote_identifier="hpr28u", rule__type=RuleTypeChoices.subreddit remote_identifier="hpr28u", rule__type=RuleTypeChoices.subreddit
) )
self.assertEquals( self.assertEqual(
post.publication_date, pytz.utc.localize(datetime(2020, 7, 12, 10, 29, 10)) post.publication_date, datetime(2020, 7, 12, 10, 29, 10, tzinfo=timezone.utc)
) )
self.assertEquals(post.author, "Sebaron") self.assertEqual(post.author, "Sebaron")
self.assertEquals( self.assertEqual(
post.title, post.title,
"I am a medical student, and I recently programmed an open-source eye-tracker for brain research", "I am a medical student, and I recently programmed an open-source eye-tracker for brain research",
) )
self.assertEquals( self.assertEqual(
post.url, post.url,
"https://www.reddit.com/r/Python/comments/hpr28u/i_am_a_medical_student_and_i_recently_programmed/", "https://www.reddit.com/r/Python/comments/hpr28u/i_am_a_medical_student_and_i_recently_programmed/",
) )
@ -128,13 +125,13 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector() collector = RedditCollector()
collector.collect(rules=rules) collector.collect(rules=rules)
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
for subreddit in rules: for subreddit in rules:
with self.subTest(subreddit=subreddit): with self.subTest(subreddit=subreddit):
self.assertEquals(subreddit.succeeded, True) self.assertEqual(subreddit.succeeded, True)
self.assertEquals(subreddit.last_run, timezone.now()) self.assertEqual(subreddit.last_run, datetime.now(tz=timezone.utc))
self.assertEquals(subreddit.error, None) self.assertEqual(subreddit.error, None)
def test_not_found(self): def test_not_found(self):
self.mocked_fetch.side_effect = StreamNotFoundException self.mocked_fetch.side_effect = StreamNotFoundException
@ -148,9 +145,9 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector() collector = RedditCollector()
collector.collect(rules=((rule,),)) collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream not found") self.assertEqual(rule.error, "Stream not found")
@patch("newsreader.news.collection.reddit.RedditTokenTask") @patch("newsreader.news.collection.reddit.RedditTokenTask")
def test_denied(self, mocked_task): def test_denied(self, mocked_task):
@ -165,9 +162,9 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector() collector = RedditCollector()
collector.collect(rules=((rule,),)) collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream does not have sufficient permissions") self.assertEqual(rule.error, "Stream does not have sufficient permissions")
mocked_task.delay.assert_called_once_with(rule.user.pk) mocked_task.delay.assert_called_once_with(rule.user.pk)
@ -183,9 +180,9 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector() collector = RedditCollector()
collector.collect(rules=((rule,),)) collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream forbidden") self.assertEqual(rule.error, "Stream forbidden")
def test_timed_out(self): def test_timed_out(self):
self.mocked_fetch.side_effect = StreamTimeOutException self.mocked_fetch.side_effect = StreamTimeOutException
@ -199,6 +196,6 @@ class RedditCollectorTestCase(TestCase):
collector = RedditCollector() collector = RedditCollector()
collector.collect(rules=((rule,),)) collector.collect(rules=((rule,),))
self.assertEquals(Post.objects.count(), 0) self.assertEqual(Post.objects.count(), 0)
self.assertEquals(rule.succeeded, False) self.assertEqual(rule.succeeded, False)
self.assertEquals(rule.error, "Stream timed out") self.assertEqual(rule.error, "Stream timed out")

View file

@ -46,7 +46,6 @@ class CollectionRuleViewTestCase:
name="new name", name="new name",
category=other_rule.category, category=other_rule.category,
url=other_rule.url, url=other_rule.url,
timezone=other_rule.timezone,
) )
other_url = reverse("news:collection:feed-update", args=[other_rule.pk]) other_url = reverse("news:collection:feed-update", args=[other_rule.pk])

View file

@ -1,8 +1,6 @@
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
import pytz
from django_celery_beat.models import PeriodicTask from django_celery_beat.models import PeriodicTask
from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.choices import RuleTypeChoices
@ -21,23 +19,21 @@ class FeedCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update( self.form_data.update(
name="new rule", name="new rule",
url="https://www.rss.com/rss", url="https://www.rss.com/rss",
timezone=pytz.utc,
category=str(self.category.pk), category=str(self.category.pk),
) )
def test_creation(self): def test_creation(self):
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
rule = CollectionRule.objects.get(name="new rule") rule = CollectionRule.objects.get(name="new rule")
self.assertEquals(rule.type, RuleTypeChoices.feed) self.assertEqual(rule.type, RuleTypeChoices.feed)
self.assertEquals(rule.url, "https://www.rss.com/rss") self.assertEqual(rule.url, "https://www.rss.com/rss")
self.assertEquals(rule.timezone, str(pytz.utc)) self.assertEqual(rule.favicon, None)
self.assertEquals(rule.favicon, None) self.assertEqual(rule.category.pk, self.category.pk)
self.assertEquals(rule.category.pk, self.category.pk) self.assertEqual(rule.user.pk, self.user.pk)
self.assertEquals(rule.user.pk, self.user.pk)
self.assertTrue( self.assertTrue(
PeriodicTask.objects.get( PeriodicTask.objects.get(
@ -59,18 +55,17 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
name=self.rule.name, name=self.rule.name,
category=self.rule.category.pk, category=self.rule.category.pk,
url=self.rule.url, url=self.rule.url,
timezone=self.rule.timezone,
) )
def test_name_change(self): def test_name_change(self):
self.form_data.update(name="new name") self.form_data.update(name="new name")
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db() self.rule.refresh_from_db()
self.assertEquals(self.rule.name, "new name") self.assertEqual(self.rule.name, "new name")
def test_category_change(self): def test_category_change(self):
new_category = CategoryFactory(user=self.user) new_category = CategoryFactory(user=self.user)
@ -78,21 +73,21 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(category=new_category.pk) self.form_data.update(category=new_category.pk)
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db() self.rule.refresh_from_db()
self.assertEquals(self.rule.category.pk, new_category.pk) self.assertEqual(self.rule.category.pk, new_category.pk)
def test_category_removal(self): def test_category_removal(self):
self.form_data.update(category="") self.form_data.update(category="")
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db() self.rule.refresh_from_db()
self.assertEquals(self.rule.category, None) self.assertEqual(self.rule.category, None)
def test_rules_only(self): def test_rules_only(self):
rule = FeedFactory( rule = FeedFactory(
@ -106,4 +101,4 @@ class FeedUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
response = self.client.get(url) response = self.client.get(url)
self.assertEquals(response.status_code, 404) self.assertEqual(response.status_code, 404)

View file

@ -2,8 +2,6 @@ from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
import pytz
from newsreader.news.collection.choices import RuleTypeChoices from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL
@ -32,16 +30,15 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
def test_creation(self): def test_creation(self):
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
rule = CollectionRule.objects.get(name="new rule") rule = CollectionRule.objects.get(name="new rule")
self.assertEquals(rule.type, RuleTypeChoices.subreddit) self.assertEqual(rule.type, RuleTypeChoices.subreddit)
self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") self.assertEqual(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEquals(rule.timezone, str(pytz.utc)) self.assertEqual(rule.favicon, None)
self.assertEquals(rule.favicon, None) self.assertEqual(rule.category.pk, self.category.pk)
self.assertEquals(rule.category.pk, self.category.pk) self.assertEqual(rule.user.pk, self.user.pk)
self.assertEquals(rule.user.pk, self.user.pk)
def test_regular_reddit_url(self): def test_regular_reddit_url(self):
self.form_data.update(url=f"{REDDIT_URL}/r/aww") self.form_data.update(url=f"{REDDIT_URL}/r/aww")
@ -70,7 +67,6 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
"name": self.rule.name, "name": self.rule.name,
"url": self.rule.url, "url": self.rule.url,
"category": str(self.category.pk), "category": str(self.category.pk),
"timezone": pytz.utc,
"reddit_allow_nfsw": False, "reddit_allow_nfsw": False,
"reddit_allow_spoiler": False, "reddit_allow_spoiler": False,
"reddit_allow_viewed": True, "reddit_allow_viewed": True,
@ -82,11 +78,11 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(name="Python 2") self.form_data.update(name="Python 2")
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db() self.rule.refresh_from_db()
self.assertEquals(self.rule.name, "Python 2") self.assertEqual(self.rule.name, "Python 2")
def test_category_change(self): def test_category_change(self):
new_category = CategoryFactory(user=self.user) new_category = CategoryFactory(user=self.user)
@ -94,11 +90,11 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data.update(category=new_category.pk) self.form_data.update(category=new_category.pk)
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.rule.refresh_from_db() self.rule.refresh_from_db()
self.assertEquals(self.rule.category.pk, new_category.pk) self.assertEqual(self.rule.category.pk, new_category.pk)
def test_subreddit_rules_only(self): def test_subreddit_rules_only(self):
rule = SubredditFactory( rule = SubredditFactory(
@ -112,23 +108,22 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
response = self.client.get(url) response = self.client.get(url)
self.assertEquals(response.status_code, 404) self.assertEqual(response.status_code, 404)
def test_url_change(self): def test_url_change(self):
self.form_data.update(name="aww", url=f"{REDDIT_API_URL}/r/aww") self.form_data.update(name="aww", url=f"{REDDIT_API_URL}/r/aww")
response = self.client.post(self.url, self.form_data) response = self.client.post(self.url, self.form_data)
self.assertEquals(response.status_code, 302) self.assertEqual(response.status_code, 302)
rule = CollectionRule.objects.get(name="aww") rule = CollectionRule.objects.get(name="aww")
self.assertEquals(rule.type, RuleTypeChoices.subreddit) self.assertEqual(rule.type, RuleTypeChoices.subreddit)
self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww") self.assertEqual(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEquals(rule.timezone, str(pytz.utc)) self.assertEqual(rule.favicon, None)
self.assertEquals(rule.favicon, None) self.assertEqual(rule.category.pk, self.category.pk)
self.assertEquals(rule.category.pk, self.category.pk) self.assertEqual(rule.user.pk, self.user.pk)
self.assertEquals(rule.user.pk, self.user.pk)
def test_regular_reddit_url(self): def test_regular_reddit_url(self):
self.form_data.update(url=f"{REDDIT_URL}/r/aww") self.form_data.update(url=f"{REDDIT_URL}/r/aww")

View file

@ -1,10 +1,8 @@
from datetime import datetime from datetime import datetime, timezone
from django.conf import settings from django.conf import settings
from django.db.models.fields import CharField, TextField from django.db.models.fields import CharField, TextField
from django.utils import timezone
import pytz
import requests import requests
from requests.exceptions import RequestException from requests.exceptions import RequestException
@ -15,14 +13,11 @@ from newsreader.news.collection.response_handler import ResponseHandler
DEFAULT_HEADERS = {"User-Agent": f"linux:rss.fudiggity.nl:{settings.VERSION}"} DEFAULT_HEADERS = {"User-Agent": f"linux:rss.fudiggity.nl:{settings.VERSION}"}
def build_publication_date(dt, tz): def build_publication_date(_datetime_info: tuple) -> datetime:
try: try:
naive_datetime = datetime(*dt[:6]) return datetime(*_datetime_info[:6], tzinfo=timezone.utc)
published_parsed = timezone.make_aware(naive_datetime, timezone=tz)
except (TypeError, ValueError): except (TypeError, ValueError):
return timezone.now() return datetime.now(tz=timezone.utc)
return published_parsed.astimezone(pytz.utc)
def fetch(url, auth=None, headers={}): def fetch(url, auth=None, headers={}):

View file

@ -1,8 +1,8 @@
import json import json
from django.urls import reverse_lazy from zoneinfo import available_timezones
import pytz from django.urls import reverse_lazy
from django_celery_beat.models import IntervalSchedule, PeriodicTask from django_celery_beat.models import IntervalSchedule, PeriodicTask
@ -25,7 +25,7 @@ class CollectionRuleDetailMixin:
context_data = super().get_context_data(**kwargs) context_data = super().get_context_data(**kwargs)
categories = Category.objects.filter(user=self.request.user).order_by("name") categories = Category.objects.filter(user=self.request.user).order_by("name")
timezones = [timezone for timezone in pytz.all_timezones] timezones = available_timezones()
context_data["categories"] = categories context_data["categories"] = categories
context_data["timezones"] = timezones context_data["timezones"] = timezones

View file

@ -1,5 +1,4 @@
from django.utils.encoding import force_text from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from rest_framework import filters from rest_framework import filters
from rest_framework.compat import coreapi, coreschema from rest_framework.compat import coreapi, coreschema
@ -25,8 +24,8 @@ class ReadFilter(filters.BaseFilterBackend):
required=False, required=False,
location="query", location="query",
schema=coreschema.String( schema=coreschema.String(
title=force_text(self.query_param), title=str(self.query_param),
description=force_text(_("Wether posts should be read or not")), description=str(_("Wether posts should be read or not")),
), ),
) )
] ]
@ -52,8 +51,8 @@ class SavedFilter(filters.BaseFilterBackend):
required=False, required=False,
location="query", location="query",
schema=coreschema.String( schema=coreschema.String(
title=force_text(self.query_param), title=str(self.query_param),
description=force_text(_("Wether posts should be saved or not")), description=str(_("Wether posts should be saved or not")),
), ),
) )
] ]

View file

@ -1,12 +1,10 @@
import json import json
from datetime import datetime from datetime import datetime, timezone
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
@ -29,15 +27,15 @@ class CategoryListViewTestCase(TestCase):
def test_ordering(self): def test_ordering(self):
categories = [ categories = [
CategoryFactory( CategoryFactory(
created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc),
user=self.user, user=self.user,
), ),
CategoryFactory( CategoryFactory(
created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc), created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=timezone.utc),
user=self.user, user=self.user,
), ),
CategoryFactory( CategoryFactory(
created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc), created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=timezone.utc),
user=self.user, user=self.user,
), ),
] ]
@ -428,37 +426,37 @@ class NestedCategoryPostView(TestCase):
FeedPostFactory.create( FeedPostFactory.create(
title="Second Reuters post", title="Second Reuters post",
rule=reuters_rule, rule=reuters_rule,
publication_date=datetime(2019, 5, 21, 15, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 21, 15, tzinfo=timezone.utc),
), ),
FeedPostFactory.create( FeedPostFactory.create(
title="First Reuters post", title="First Reuters post",
rule=reuters_rule, rule=reuters_rule,
publication_date=datetime(2019, 5, 20, 12, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 20, 12, tzinfo=timezone.utc),
), ),
] ]
FeedPostFactory.create( FeedPostFactory.create(
title="Second Guardian post", title="Second Guardian post",
rule=guardian_rule, rule=guardian_rule,
publication_date=datetime(2019, 5, 21, 14, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 21, 14, tzinfo=timezone.utc),
) )
FeedPostFactory.create( FeedPostFactory.create(
title="First Guardian post", title="First Guardian post",
rule=guardian_rule, rule=guardian_rule,
publication_date=datetime(2019, 5, 20, 11, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 20, 11, tzinfo=timezone.utc),
) )
FeedPostFactory.create( FeedPostFactory.create(
title="Second BBC post", title="Second BBC post",
rule=bbc_rule, rule=bbc_rule,
publication_date=datetime(2019, 5, 21, 16, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 21, 16, tzinfo=timezone.utc),
) )
FeedPostFactory.create( FeedPostFactory.create(
title="First BBC post", title="First BBC post",
rule=bbc_rule, rule=bbc_rule,
publication_date=datetime(2019, 5, 20, 13, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 20, 13, tzinfo=timezone.utc),
) )
response = self.client.get( response = self.client.get(

View file

@ -1,10 +1,8 @@
from datetime import datetime from datetime import datetime, timezone
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
@ -22,8 +20,8 @@ class PostListViewTestCase(TestCase):
response = self.client.get(reverse("api:news:core:posts-list")) response = self.client.get(reverse("api:news:core:posts-list"))
data = response.json() data = response.json()
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEquals(len(data["results"]), 3) self.assertEqual(len(data["results"]), 3)
def test_ordering(self): def test_ordering(self):
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
@ -32,24 +30,24 @@ class PostListViewTestCase(TestCase):
FeedPostFactory( FeedPostFactory(
title="I'm the first post", title="I'm the first post",
rule=rule, rule=rule,
publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 20, 16, 7, 38, tzinfo=timezone.utc),
), ),
FeedPostFactory( FeedPostFactory(
title="I'm the second post", title="I'm the second post",
rule=rule, rule=rule,
publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 20, 16, 7, 37, tzinfo=timezone.utc),
), ),
FeedPostFactory( FeedPostFactory(
title="I'm the third post", title="I'm the third post",
rule=rule, rule=rule,
publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=pytz.utc), publication_date=datetime(2019, 5, 20, 16, 7, 36, tzinfo=timezone.utc),
), ),
] ]
response = self.client.get(reverse("api:news:core:posts-list")) response = self.client.get(reverse("api:news:core:posts-list"))
data = response.json() data = response.json()
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
for index, post in enumerate(posts, start=0): for index, post in enumerate(posts, start=0):
with self.subTest(post=post): with self.subTest(post=post):
@ -68,8 +66,8 @@ class PostListViewTestCase(TestCase):
data = response.json() data = response.json()
posts = data["results"] posts = data["results"]
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEquals(len(data["results"]), 10) self.assertEqual(len(data["results"]), 10)
for post in posts: for post in posts:
with self.subTest(post=post): with self.subTest(post=post):
@ -88,8 +86,8 @@ class PostListViewTestCase(TestCase):
data = response.json() data = response.json()
posts = data["results"] posts = data["results"]
self.assertEquals(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEquals(len(data["results"]), 10) self.assertEqual(len(data["results"]), 10)
for post in posts: for post in posts:
with self.subTest(post=post): with self.subTest(post=post):

View file

@ -1,6 +1,7 @@
from datetime import timezone
import factory import factory
import factory.fuzzy import factory.fuzzy
import pytz
from newsreader.accounts.tests.factories import UserFactory from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.reddit import REDDIT_API_URL from newsreader.news.collection.reddit import REDDIT_API_URL
@ -19,7 +20,7 @@ class PostFactory(factory.django.DjangoModelFactory):
title = factory.Faker("sentence") title = factory.Faker("sentence")
body = factory.Faker("paragraph") body = factory.Faker("paragraph")
author = factory.Faker("name") author = factory.Faker("name")
publication_date = factory.Faker("date_time_this_year", tzinfo=pytz.utc) publication_date = factory.Faker("date_time_this_year", tzinfo=timezone.utc)
url = factory.Faker("url") url = factory.Faker("url")
remote_identifier = factory.Faker("uuid4") remote_identifier = factory.Faker("uuid4")

222
uv.lock generated
View file

@ -97,9 +97,6 @@ version = "3.3.2"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 },
{ url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 },
{ url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 },
{ url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 },
{ url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 },
{ url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 },
@ -110,11 +107,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 },
{ url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 },
{ url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 },
{ url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 },
{ url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 },
{ url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 },
{ url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 },
{ url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 },
{ url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 },
{ url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 },
{ url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 },
@ -125,8 +117,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 },
{ url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 },
{ url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 },
{ url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 },
{ url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 },
{ url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 },
] ]
@ -194,46 +184,30 @@ version = "7.6.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 } sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796 },
{ url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244 },
{ url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 }, { url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 },
{ url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859 }, { url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859 },
{ url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549 }, { url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549 },
{ url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477 }, { url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477 },
{ url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134 }, { url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134 },
{ url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910 }, { url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910 },
{ url = "https://files.pythonhosted.org/packages/7d/30/033e663399ff17dca90d793ee8a2ea2890e7fdf085da58d82468b4220bf7/coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", size = 209348 },
{ url = "https://files.pythonhosted.org/packages/20/05/0d1ccbb52727ccdadaa3ff37e4d2dc1cd4d47f0c3df9eb58d9ec8508ca88/coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", size = 210230 },
{ url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 },
{ url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 },
{ url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 }, { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 },
{ url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 }, { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 },
{ url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 }, { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 },
{ url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 }, { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 },
{ url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 }, { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 },
{ url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 }, { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 },
{ url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 },
{ url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 },
{ url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 },
{ url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 },
{ url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 }, { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 },
{ url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 }, { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 },
{ url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 }, { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 },
{ url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 }, { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 },
{ url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 }, { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 },
{ url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 }, { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 },
{ url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 },
{ url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 },
{ url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 },
{ url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 },
{ url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 }, { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 },
{ url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 }, { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 },
{ url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 }, { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 },
{ url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 }, { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 },
{ url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 }, { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 },
{ url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 }, { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 },
{ url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 },
{ url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 },
] ]
[[package]] [[package]]
@ -247,16 +221,15 @@ wheels = [
[[package]] [[package]]
name = "django" name = "django"
version = "3.2.25" version = "4.2.16"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "asgiref", marker = "sys_platform == 'linux'" }, { name = "asgiref", marker = "sys_platform == 'linux'" },
{ name = "pytz", marker = "sys_platform == 'linux'" },
{ name = "sqlparse", marker = "sys_platform == 'linux'" }, { name = "sqlparse", marker = "sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ec/68/0e744f07b57bfdf99abbb6b3eb14fcba188867021c05f4a104e04f6d56b8/Django-3.2.25.tar.gz", hash = "sha256:7ca38a78654aee72378594d63e51636c04b8e28574f5505dff630895b5472777", size = 9836336 } sdist = { url = "https://files.pythonhosted.org/packages/65/d8/a607ee443b54a4db4ad28902328b906ae6218aa556fb9b3ac45c0bcb313d/Django-4.2.16.tar.gz", hash = "sha256:6f1616c2786c408ce86ab7e10f792b8f15742f7b7b7460243929cb371e7f1dad", size = 10436023 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/30/8e/cc23c762c5dcd1d367d73cf006a326e0df2bd0e785cba18b658b39904c1e/Django-3.2.25-py3-none-any.whl", hash = "sha256:a52ea7fcf280b16f7b739cec38fa6d3f8953a5456986944c3ca97e79882b4e38", size = 7890550 }, { url = "https://files.pythonhosted.org/packages/94/2c/6b6c7e493d5ea789416918658ebfa16be7a64c77610307497ed09a93c8c4/Django-4.2.16-py3-none-any.whl", hash = "sha256:1ddc333a16fc139fd253035a1606bb24261951bbc3a6ca256717fa06cc41a898", size = 7992936 },
] ]
[[package]] [[package]]
@ -274,7 +247,7 @@ wheels = [
[[package]] [[package]]
name = "django-celery-beat" name = "django-celery-beat"
version = "2.5.0" version = "2.7.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "celery", marker = "sys_platform == 'linux'" }, { name = "celery", marker = "sys_platform == 'linux'" },
@ -284,9 +257,9 @@ dependencies = [
{ name = "python-crontab", marker = "sys_platform == 'linux'" }, { name = "python-crontab", marker = "sys_platform == 'linux'" },
{ name = "tzdata", marker = "sys_platform == 'linux'" }, { name = "tzdata", marker = "sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/0b/97/ca63898f76dd43fc91f4791b05dbbecb60dc99215f16b270e9b1e29af974/django-celery-beat-2.5.0.tar.gz", hash = "sha256:cd0a47f5958402f51ac0c715bc942ae33d7b50b4e48cba91bc3f2712be505df1", size = 159635 } sdist = { url = "https://files.pythonhosted.org/packages/03/8f/8a18f234173001bd7a7d63826d2d7f456b38031c892514d27c0f7aea10be/django_celery_beat-2.7.0.tar.gz", hash = "sha256:8482034925e09b698c05ad61c36ed2a8dbc436724a3fe119215193a4ca6dc967", size = 163472 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/92/fa53396870566276357bb81e3fece5b7f8a00f99c91689ff777c481d40e0/django_celery_beat-2.5.0-py3-none-any.whl", hash = "sha256:ae460faa5ea142fba0875409095d22f6bd7bcc7377889b85e8cab5c0dfb781fe", size = 97223 }, { url = "https://files.pythonhosted.org/packages/29/f8/f5a25472222b19258c3a53ce71c4efd171a12ab3c988bb3026dec0522a64/django_celery_beat-2.7.0-py3-none-any.whl", hash = "sha256:851c680d8fbf608ca5fecd5836622beea89fa017bc2b3f94a5b8c648c32d84b1", size = 94097 },
] ]
[[package]] [[package]]
@ -332,6 +305,35 @@ dependencies = [
] ]
sdist = { url = "https://files.pythonhosted.org/packages/ed/d2/61159bc6efd1bf16adc4a2a48f7ace2080d1f7aef054f606d1857cab490c/django-rest-framework-0.1.0.tar.gz", hash = "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a", size = 969 } sdist = { url = "https://files.pythonhosted.org/packages/ed/d2/61159bc6efd1bf16adc4a2a48f7ace2080d1f7aef054f606d1857cab490c/django-rest-framework-0.1.0.tar.gz", hash = "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a", size = 969 }
[[package]]
name = "django-stubs"
version = "5.0.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref", marker = "sys_platform == 'linux'" },
{ name = "django", marker = "sys_platform == 'linux'" },
{ name = "django-stubs-ext", marker = "sys_platform == 'linux'" },
{ name = "types-pyyaml", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/36/f4/2dfc77809e4d04164ec614755e2359ec2e68a32f7b5428909fa0b7f8f4e0/django_stubs-5.0.4.tar.gz", hash = "sha256:78e3764488fdfd2695f12502136548ec22f8d4b1780541a835042b8238d11514", size = 262238 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/f0/36c0f82ed7b4ef630b39e165590645c4fe4361f52d41bca5001327d62f57/django_stubs-5.0.4-py3-none-any.whl", hash = "sha256:c2502f5ecbae50c68f9a86d52b5b2447d8648fd205036dad0ccb41e19a445927", size = 466530 },
]
[[package]]
name = "django-stubs-ext"
version = "5.0.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django", marker = "sys_platform == 'linux'" },
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/91/77/ef453a8286fff87db8efd7fe93c1a86f05aeddcc78973c883af91b667f74/django_stubs_ext-5.0.4.tar.gz", hash = "sha256:85da065224204774208be29c7d02b4482d5a69218a728465c2fbe41725fdc819", size = 9410 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/54/66a7ccb1f4e4a8e37e0881a3dfdcabaee9fc0c0c91cbe64170e794acebd7/django_stubs_ext-5.0.4-py3-none-any.whl", hash = "sha256:910cbaff3d1e8e806a5c27d5ddd4088535aae8371ea921b7fd680fdfa5f14e30", size = 8954 },
]
[[package]] [[package]]
name = "django-timezone-field" name = "django-timezone-field"
version = "7.0" version = "7.0"
@ -406,23 +408,26 @@ wheels = [
[[package]] [[package]]
name = "ftfy" name = "ftfy"
version = "5.9" version = "6.2.3"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "wcwidth", marker = "sys_platform == 'linux'" }, { name = "wcwidth", marker = "sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/04/06/e5c80e2e0f979628d47345efba51f7ba386fe95963b11c594209085f5a9b/ftfy-5.9.tar.gz", hash = "sha256:8c4fb2863c0b82eae2ab3cf353d9ade268dfbde863d322f78d6a9fd5cefb31e9", size = 66049 } sdist = { url = "https://files.pythonhosted.org/packages/da/a9/59f4354257e8350a25be1774021991fb3a99a2fb87d0c1f367592548aed3/ftfy-6.2.3.tar.gz", hash = "sha256:79b505988f29d577a58a9069afe75553a02a46e42de6091c0660cdc67812badc", size = 64165 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ed/46/14d230ad057048aea7ccd2f96a80905830866d281ea90a6662a825490659/ftfy-6.2.3-py3-none-any.whl", hash = "sha256:f15761b023f3061a66207d33f0c0149ad40a8319fd16da91796363e2c049fdf8", size = 43011 },
]
[[package]] [[package]]
name = "gunicorn" name = "gunicorn"
version = "20.1.0" version = "23.0.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "setuptools", marker = "sys_platform == 'linux'" }, { name = "packaging", marker = "sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/28/5b/0d1f0296485a6af03366604142ea8f19f0833894db3512a40ed07b2a56dd/gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8", size = 370601 } sdist = { url = "https://files.pythonhosted.org/packages/34/72/9614c465dc206155d93eff0ca20d42e1e35afc533971379482de953521a4/gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec", size = 375031 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/dd/5b190393e6066286773a67dfcc2f9492058e9b57c4867a95f1ba5caf0a83/gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", size = 79531 }, { url = "https://files.pythonhosted.org/packages/cb/7d/6dac2a6e1eba33ee43f318edbed4ff29151a49b5d37f080aad1e6469bca4/gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", size = 85029 },
] ]
[[package]] [[package]]
@ -453,8 +458,6 @@ version = "5.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 } sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", size = 8141056 },
{ url = "https://files.pythonhosted.org/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", size = 4425238 },
{ url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197 }, { url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197 },
{ url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809 }, { url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809 },
{ url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593 }, { url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593 },
@ -468,10 +471,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531 }, { url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531 },
{ url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065 }, { url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065 },
{ url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775 }, { url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775 },
{ url = "https://files.pythonhosted.org/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", size = 3474226 },
{ url = "https://files.pythonhosted.org/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", size = 3814971 },
{ url = "https://files.pythonhosted.org/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", size = 8171753 },
{ url = "https://files.pythonhosted.org/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", size = 4441955 },
{ url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778 }, { url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778 },
{ url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628 }, { url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628 },
{ url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215 }, { url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215 },
@ -485,10 +484,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341 }, { url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341 },
{ url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539 }, { url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539 },
{ url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 }, { url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 },
{ url = "https://files.pythonhosted.org/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454 },
{ url = "https://files.pythonhosted.org/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857 },
{ url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 },
{ url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 },
{ url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 }, { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 },
{ url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 }, { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 },
{ url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 }, { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 },
@ -502,14 +497,12 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 }, { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 },
{ url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 }, { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 },
{ url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 }, { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 },
{ url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 },
{ url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 },
] ]
[[package]] [[package]]
name = "newsreader" name = "newsreader"
version = "0.4.4" version = "0.4.4"
source = { editable = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "beautifulsoup4", marker = "sys_platform == 'linux'" }, { name = "beautifulsoup4", marker = "sys_platform == 'linux'" },
{ name = "bleach", marker = "sys_platform == 'linux'" }, { name = "bleach", marker = "sys_platform == 'linux'" },
@ -522,12 +515,11 @@ dependencies = [
{ name = "feedparser", marker = "sys_platform == 'linux'" }, { name = "feedparser", marker = "sys_platform == 'linux'" },
{ name = "ftfy", marker = "sys_platform == 'linux'" }, { name = "ftfy", marker = "sys_platform == 'linux'" },
{ name = "lxml", marker = "sys_platform == 'linux'" }, { name = "lxml", marker = "sys_platform == 'linux'" },
{ name = "psycopg2", marker = "sys_platform == 'linux'" }, { name = "psycopg", marker = "sys_platform == 'linux'" },
{ name = "pymemcache", marker = "sys_platform == 'linux'" },
{ name = "python-dotenv", marker = "sys_platform == 'linux'" }, { name = "python-dotenv", marker = "sys_platform == 'linux'" },
{ name = "python-memcached", marker = "sys_platform == 'linux'" },
{ name = "requests", marker = "sys_platform == 'linux'" }, { name = "requests", marker = "sys_platform == 'linux'" },
{ name = "requests-oauthlib", marker = "sys_platform == 'linux'" }, { name = "requests-oauthlib", marker = "sys_platform == 'linux'" },
{ name = "setuptools", marker = "sys_platform == 'linux'" },
] ]
[package.optional-dependencies] [package.optional-dependencies]
@ -537,6 +529,7 @@ ci = [
development = [ development = [
{ name = "django-debug-toolbar", marker = "sys_platform == 'linux'" }, { name = "django-debug-toolbar", marker = "sys_platform == 'linux'" },
{ name = "django-extensions", marker = "sys_platform == 'linux'" }, { name = "django-extensions", marker = "sys_platform == 'linux'" },
{ name = "django-stubs", marker = "sys_platform == 'linux'" },
] ]
production = [ production = [
{ name = "gunicorn", marker = "sys_platform == 'linux'" }, { name = "gunicorn", marker = "sys_platform == 'linux'" },
@ -553,29 +546,29 @@ testing = [
requires-dist = [ requires-dist = [
{ name = "beautifulsoup4" }, { name = "beautifulsoup4" },
{ name = "bleach" }, { name = "bleach" },
{ name = "celery", specifier = "~=5.0" }, { name = "celery", specifier = "~=5.4" },
{ name = "coverage", marker = "extra == 'ci'", specifier = ">=5.3.1" }, { name = "coverage", marker = "extra == 'ci'", specifier = "~=7.6.1" },
{ name = "django", specifier = "~=3.2" }, { name = "django", specifier = "~=4.2" },
{ name = "django-axes" }, { name = "django-axes" },
{ name = "django-celery-beat", specifier = "~=2.5.0" }, { name = "django-celery-beat", specifier = "~=2.7.0" },
{ name = "django-debug-toolbar", marker = "extra == 'development'" }, { name = "django-debug-toolbar", marker = "extra == 'development'" },
{ name = "django-extensions", marker = "extra == 'development'" }, { name = "django-extensions", marker = "extra == 'development'" },
{ name = "django-registration-redux", specifier = "~=2.7" }, { name = "django-registration-redux", specifier = "~=2.7" },
{ name = "django-rest-framework" }, { name = "django-rest-framework" },
{ name = "django-stubs", marker = "extra == 'development'" },
{ name = "factory-boy", marker = "extra == 'testing'" }, { name = "factory-boy", marker = "extra == 'testing'" },
{ name = "feedparser" }, { name = "feedparser" },
{ name = "freezegun", marker = "extra == 'testing'" }, { name = "freezegun", marker = "extra == 'testing'" },
{ name = "ftfy", specifier = "~=5.8" }, { name = "ftfy", specifier = "~=6.2" },
{ name = "gunicorn", marker = "extra == 'production'", specifier = "~=20.0" }, { name = "gunicorn", marker = "extra == 'production'", specifier = "~=23.0" },
{ name = "lxml" }, { name = "lxml" },
{ name = "psycopg2" }, { name = "psycopg" },
{ name = "python-dotenv", specifier = "~=0.12" }, { name = "pymemcache" },
{ name = "python-memcached", specifier = "<=1.59" }, { name = "python-dotenv", specifier = "~=1.0.1" },
{ name = "requests" }, { name = "requests" },
{ name = "requests-oauthlib" }, { name = "requests-oauthlib" },
{ name = "ruff", marker = "extra == 'testing'", specifier = ">=0.6.3" }, { name = "ruff", marker = "extra == 'testing'", specifier = ">=0.6.3" },
{ name = "sentry-sdk", marker = "extra == 'production'", specifier = "~=1.0" }, { name = "sentry-sdk", marker = "extra == 'production'", specifier = "~=2.0" },
{ name = "setuptools", specifier = ">=74.0.0" },
{ name = "tblib", marker = "extra == 'testing'" }, { name = "tblib", marker = "extra == 'testing'" },
] ]
@ -588,6 +581,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 },
] ]
[[package]]
name = "packaging"
version = "24.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
]
[[package]] [[package]]
name = "prompt-toolkit" name = "prompt-toolkit"
version = "3.0.47" version = "3.0.47"
@ -601,15 +603,24 @@ wheels = [
] ]
[[package]] [[package]]
name = "psycopg2" name = "psycopg"
version = "2.9.9" version = "3.2.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c9/5e/dc6acaf46d78979d6b03458b7a1618a68e152a6776fce95daac5e0f0301b/psycopg2-2.9.9.tar.gz", hash = "sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156", size = 384926 } dependencies = [
{ name = "typing-extensions", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ff/8e/f176997fd790d3dce9fa0ca695391beaeee39af7ecd6d426c4c063cf6744/psycopg-3.2.1.tar.gz", hash = "sha256:dc8da6dc8729dacacda3cc2f17d2c9397a70a66cf0d2b69c91065d60d5f00cb7", size = 155313 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/91/2c/1fc5b9d33cd248c548ba19f2cef8e89cabaafab9858a602868a592cdc1b0/psycopg2-2.9.9-cp311-cp311-win32.whl", hash = "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372", size = 1024163 }, { url = "https://files.pythonhosted.org/packages/8a/0e/0f755db36f47f96464463385552f8f132a981731356837c9a30a11ab2d35/psycopg-3.2.1-py3-none-any.whl", hash = "sha256:ece385fb413a37db332f97c49208b36cf030ff02b199d7635ed2fbd378724175", size = 197743 },
{ url = "https://files.pythonhosted.org/packages/37/2c/5133dd3183a3bd82371569f0dd783e6927672de7e671b278ce248810b7f7/psycopg2-2.9.9-cp311-cp311-win_amd64.whl", hash = "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981", size = 1163579 }, ]
{ url = "https://files.pythonhosted.org/packages/13/13/f74ffe6b6f58822e807c70391dc5679a53feb92ce119ccb8a6546c3fb893/psycopg2-2.9.9-cp312-cp312-win32.whl", hash = "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024", size = 1024634 },
{ url = "https://files.pythonhosted.org/packages/58/4b/c4a26e191882b60150bfcb639e416524ae7f8249ab7ee854fb5247f16c40/psycopg2-2.9.9-cp312-cp312-win_amd64.whl", hash = "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693", size = 1163789 }, [[package]]
name = "pymemcache"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d9/b6/4541b664aeaad025dfb8e851dcddf8e25ab22607e674dd2b562ea3e3586f/pymemcache-4.0.0.tar.gz", hash = "sha256:27bf9bd1bbc1e20f83633208620d56de50f14185055e49504f4f5e94e94aff94", size = 70176 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/41/ba/2f7b22d8135b51c4fefb041461f8431e1908778e6539ff5af6eeaaee367a/pymemcache-4.0.0-py2.py3-none-any.whl", hash = "sha256:f507bc20e0dc8d562f8df9d872107a278df049fa496805c1431b926f3ddd0eab", size = 60772 },
] ]
[[package]] [[package]]
@ -638,32 +649,11 @@ wheels = [
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "0.21.1" version = "1.0.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/d7/d548e0d5a68b328a8d69af833a861be415a17cb15ce3d8f0cd850073d2e1/python-dotenv-0.21.1.tar.gz", hash = "sha256:1c93de8f636cde3ce377292818d0e440b6e45a82f215c3744979151fa8151c49", size = 35930 } sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/64/62/f19d1e9023aacb47241de3ab5a5d5fedf32c78a71a9e365bb2153378c141/python_dotenv-0.21.1-py3-none-any.whl", hash = "sha256:41e12e0318bebc859fcc4d97d4db8d20ad21721a6aa5047dd59f090391cb549a", size = 19284 }, { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
]
[[package]]
name = "python-memcached"
version = "1.59"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "six", marker = "sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz", hash = "sha256:a2e28637be13ee0bf1a8b6843e7490f9456fd3f2a4cb60471733c7b5d5557e4f", size = 22210 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f5/90/19d3908048f70c120ec66a39e61b92c253e834e6e895cd104ce5e46cbe53/python_memcached-1.59-py2.py3-none-any.whl", hash = "sha256:4dac64916871bd3550263323fc2ce18e1e439080a2d5670c594cf3118d99b594", size = 16200 },
]
[[package]]
name = "pytz"
version = "2024.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 },
] ]
[[package]] [[package]]
@ -701,8 +691,6 @@ source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5d/f9/0b32e5d1c6f957df49398cd882a011e9488fcbca0d6acfeeea50ccd37a4d/ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983", size = 2463514 } sdist = { url = "https://files.pythonhosted.org/packages/5d/f9/0b32e5d1c6f957df49398cd882a011e9488fcbca0d6acfeeea50ccd37a4d/ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983", size = 2463514 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/72/68/1da6a1e39a03a229ea57c511691d6225072759cc7764206c3f0989521194/ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3", size = 9696928 }, { url = "https://files.pythonhosted.org/packages/72/68/1da6a1e39a03a229ea57c511691d6225072759cc7764206c3f0989521194/ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3", size = 9696928 },
{ url = "https://files.pythonhosted.org/packages/6e/59/3b8b1d3a4271c6eb6ceecd3cef19a6d881639a0f18ad651563d6f619aaae/ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc", size = 9448462 },
{ url = "https://files.pythonhosted.org/packages/35/4f/b942ecb8bbebe53aa9b33e9b96df88acd50b70adaaed3070f1d92131a1cb/ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1", size = 9176190 },
{ url = "https://files.pythonhosted.org/packages/a0/20/b0bcb29d4ee437f3567b73b6905c034e2e94d29b9b826c66daecc1cf6388/ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1", size = 10108892 }, { url = "https://files.pythonhosted.org/packages/a0/20/b0bcb29d4ee437f3567b73b6905c034e2e94d29b9b826c66daecc1cf6388/ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1", size = 10108892 },
{ url = "https://files.pythonhosted.org/packages/9c/e3/211bc759f424e8823a9937e0f678695ca02113c621dfde1fa756f9f26f6d/ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672", size = 9476471 }, { url = "https://files.pythonhosted.org/packages/9c/e3/211bc759f424e8823a9937e0f678695ca02113c621dfde1fa756f9f26f6d/ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672", size = 9476471 },
{ url = "https://files.pythonhosted.org/packages/b2/a3/2ec35a2d7a554364864206f0e46812b92a074ad8a014b923d821ead532aa/ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1", size = 10294802 }, { url = "https://files.pythonhosted.org/packages/b2/a3/2ec35a2d7a554364864206f0e46812b92a074ad8a014b923d821ead532aa/ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1", size = 10294802 },
@ -714,31 +702,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a8/9f/f801a1619f5549e552f1f722f1db57eb39e7e1d83d482133142781d450de/ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5", size = 9563859 }, { url = "https://files.pythonhosted.org/packages/a8/9f/f801a1619f5549e552f1f722f1db57eb39e7e1d83d482133142781d450de/ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5", size = 9563859 },
{ url = "https://files.pythonhosted.org/packages/0b/4d/fb2424faf04ffdb960ae2b3a1d991c5183dd981003de727d2d5cc38abc98/ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351", size = 9914291 }, { url = "https://files.pythonhosted.org/packages/0b/4d/fb2424faf04ffdb960ae2b3a1d991c5183dd981003de727d2d5cc38abc98/ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351", size = 9914291 },
{ url = "https://files.pythonhosted.org/packages/2e/dd/94fddf002a8f6152e8ebfbb51d3f93febc415c1fe694345623c31ce8b33b/ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8", size = 10331549 }, { url = "https://files.pythonhosted.org/packages/2e/dd/94fddf002a8f6152e8ebfbb51d3f93febc415c1fe694345623c31ce8b33b/ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8", size = 10331549 },
{ url = "https://files.pythonhosted.org/packages/b4/73/ca9c2f9237a430ca423b6dca83b77e9a428afeb7aec80596e86c369123fe/ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521", size = 7962163 },
{ url = "https://files.pythonhosted.org/packages/55/ce/061c605b1dfb52748d59bc0c7a8507546c178801156415773d18febfd71d/ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb", size = 8800901 },
{ url = "https://files.pythonhosted.org/packages/63/28/ae4ffe7d3b6134ca6d31ebef07447ef70097c4a9e8fbbc519b374c5c1559/ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82", size = 8229171 },
] ]
[[package]] [[package]]
name = "sentry-sdk" name = "sentry-sdk"
version = "1.45.1" version = "2.13.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "certifi", marker = "sys_platform == 'linux'" }, { name = "certifi", marker = "sys_platform == 'linux'" },
{ name = "urllib3", marker = "sys_platform == 'linux'" }, { name = "urllib3", marker = "sys_platform == 'linux'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/c8/28/02c0cd9184f9108e3c52519f9628b215077a3854240e0b17ae845e664855/sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26", size = 244774 } sdist = { url = "https://files.pythonhosted.org/packages/bb/41/97f673384dae5ed81cc2a568cc5c28e76deee85f8ba50def862e86150a5a/sentry_sdk-2.13.0.tar.gz", hash = "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260", size = 279937 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/9f/105366a122efa93f0cb1914f841747d160788e4d022d0488d2d44c2ba26c/sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1", size = 267163 }, { url = "https://files.pythonhosted.org/packages/ad/7e/e9ca09f24a6c334286631a2d32c267cdc5edad5ac03fd9d20a01a82f1c35/sentry_sdk-2.13.0-py2.py3-none-any.whl", hash = "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6", size = 309078 },
]
[[package]]
name = "setuptools"
version = "74.0.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6a/21/8fd457d5a979109603e0e460c73177c3a9b6b7abcd136d0146156da95895/setuptools-74.0.0.tar.gz", hash = "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e", size = 1389536 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/df/b5/168cec9a10bf93b60b8f9af7f4e61d526e31e1aad8b9be0e30837746d700/setuptools-74.0.0-py3-none-any.whl", hash = "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f", size = 1301729 },
] ]
[[package]] [[package]]
@ -783,6 +759,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9b/87/ce70db7cae60e67851eb94e1a2127d4abb573d3866d2efd302ceb0d4d2a5/tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", size = 12478 }, { url = "https://files.pythonhosted.org/packages/9b/87/ce70db7cae60e67851eb94e1a2127d4abb573d3866d2efd302ceb0d4d2a5/tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129", size = 12478 },
] ]
[[package]]
name = "types-pyyaml"
version = "6.0.12.20240808"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/dd/08/6f5737f645571b7a0b1ebd2fe8b5cf1ee4ec3e707866ca96042a86fc1d10/types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af", size = 12359 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f3/ad/ffbad24e2bc8f20bf047ec22af0c0a92f6ce2071eb21c9103df600cda6de/types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35", size = 15298 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]] [[package]]
name = "tzdata" name = "tzdata"
version = "2024.1" version = "2024.1"