-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from zstyblik/refactoring_into_lib
Move CachedData and HTTPSource classes into separate files
- Loading branch information
Showing
23 changed files
with
373 additions
and
265 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#!/usr/bin/env python3 | ||
"""Just init. | ||
I love how black and reorder-python-imports play nicely together and no | ||
workarounds are needed what so ever. | ||
""" | ||
from .cached_data import CachedData # noqa: F401 | ||
from .http_source import HTTPSource # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#!/usr/bin/env python3 | ||
"""Code related to Cache. | ||
I love how black and reorder-python-imports play nicely together and no | ||
workarounds are needed what so ever. | ||
""" | ||
import time | ||
from dataclasses import dataclass | ||
from dataclasses import field | ||
|
||
from .config_options import DATA_SOURCE_EXPIRATION | ||
from .http_source import HTTPSource | ||
|
||
|
||
@dataclass | ||
class CachedData: | ||
"""CachedData represents locally cached data and state.""" | ||
|
||
data_sources: dict = field(default_factory=dict) | ||
items: dict = field(default_factory=dict) | ||
|
||
def get_source_by_url(self, url: str) -> HTTPSource: | ||
"""Return source by URL. | ||
If source doesn't exist, it will be created. | ||
""" | ||
source = self.data_sources.get(url, None) | ||
if source: | ||
source.last_used_ts = int(time.time()) | ||
return source | ||
|
||
self.data_sources[url] = HTTPSource( | ||
last_used_ts=int(time.time()), url=url | ||
) | ||
return self.get_source_by_url(url) | ||
|
||
def scrub_data_sources( | ||
self, expiration: int = DATA_SOURCE_EXPIRATION | ||
) -> None: | ||
"""Delete expired data sources.""" | ||
now = int(time.time()) | ||
for key in list(self.data_sources.keys()): | ||
diff = now - self.data_sources[key].last_used_ts | ||
if int(diff) > expiration: | ||
self.data_sources.pop(key) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/usr/bin/env python3 | ||
"""Common configuration options. | ||
I love how black and reorder-python-imports play nicely together and no | ||
workarounds are needed what so ever. | ||
""" | ||
CACHE_EXPIRATION = 86400 # seconds | ||
DATA_SOURCE_EXPIRATION = 30 * 86400 # seconds | ||
HTTP_TIMEOUT = 30 # seconds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#!/usr/bin/env python3 | ||
"""Code related to HTTP Source. | ||
I love how black and reorder-python-imports play nicely together and no | ||
workarounds are needed what so ever. | ||
""" | ||
from dataclasses import dataclass | ||
from dataclasses import field | ||
from typing import Dict | ||
|
||
|
||
@dataclass | ||
class HTTPSource: | ||
"""Class represents HTTP data source.""" | ||
|
||
http_etag: str = field(default_factory=str) | ||
http_last_modified: str = field(default_factory=str) | ||
last_used_ts: int = 0 | ||
url: str = field(default_factory=str) | ||
|
||
def extract_caching_headers(self, headers: Dict[str, str]) -> None: | ||
"""Extract cache related headers from given dict.""" | ||
self.http_etag = "" | ||
self.http_last_modified = "" | ||
for key, value in headers.items(): | ||
key = key.lower() | ||
if key == "etag": | ||
self.http_etag = value | ||
elif key == "last-modified": | ||
self.http_last_modified = value | ||
|
||
def make_caching_headers(self) -> Dict[str, str]: | ||
"""Return cache related headers as a dict.""" | ||
headers = {} | ||
if self.http_etag: | ||
headers["if-none-match"] = self.http_etag | ||
|
||
if self.http_last_modified: | ||
headers["if-modified-since"] = self.http_last_modified | ||
|
||
return headers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
#!/usr/bin/env python3 | ||
"""Unit tests for cached_data.py.""" | ||
import time | ||
from unittest.mock import patch | ||
|
||
from lib import CachedData | ||
from lib import config_options | ||
from lib import HTTPSource # noqa: I100 | ||
|
||
|
||
@patch("lib.cached_data.time.time") | ||
def test_cache_get_source_by_url(mock_time): | ||
"""Test that CachedData.get_source_by_url() sets last_used_ts attr.""" | ||
mock_time.return_value = 1717428213 | ||
url = "http://example.com" | ||
source = HTTPSource( | ||
last_used_ts=0, | ||
url=url, | ||
) | ||
cache = CachedData( | ||
data_sources={ | ||
url: source, | ||
} | ||
) | ||
result = cache.get_source_by_url(url) | ||
assert result == source | ||
assert result.last_used_ts == 1717428213 | ||
|
||
|
||
def test_cache_scrub_data_sources_empty(cache): | ||
"""Test that CachedData.scrub_data_sources() when there are no sources.""" | ||
cache = CachedData() | ||
assert not cache.data_sources | ||
cache.scrub_data_sources() | ||
assert not cache.data_sources | ||
|
||
|
||
def test_cache_scrub_data_sources(cache): | ||
"""Test that CachedData.scrub_data_sources() expired source is removed.""" | ||
source1_url = "http://ww1.example.com" | ||
source2_url = "http://ww2.example.com" | ||
cache = CachedData() | ||
source1 = cache.get_source_by_url(source1_url) | ||
assert source1.last_used_ts > 0 | ||
source1.last_used_ts = ( | ||
int(time.time()) - 2 * config_options.DATA_SOURCE_EXPIRATION | ||
) | ||
|
||
source2 = cache.get_source_by_url(source2_url) | ||
assert source2.last_used_ts > 0 | ||
|
||
assert "http://ww1.example.com" in cache.data_sources | ||
assert source1.url == source1_url | ||
assert "http://ww2.example.com" in cache.data_sources | ||
assert source2.url == source2_url | ||
|
||
cache.scrub_data_sources() | ||
|
||
assert "http://ww1.example.com" not in cache.data_sources | ||
assert "http://ww2.example.com" in cache.data_sources |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#!/usr/bin/env python3 | ||
"""Unit tests for http_source.py.""" | ||
import pytest | ||
|
||
from lib import HTTPSource # noqa: I202 | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"source,input_data,expected", | ||
[ | ||
# No attrs should bet set | ||
( | ||
HTTPSource(), | ||
{}, | ||
{"etag": "", "last_modified": ""}, | ||
), | ||
# Reset attrs | ||
( | ||
HTTPSource(http_etag="et_test", http_last_modified="lm_test"), | ||
{"header1": "firt", "header2": "second"}, | ||
{"etag": "", "last_modified": ""}, | ||
), | ||
# Set attrs | ||
( | ||
HTTPSource(http_etag="et_test", http_last_modified="lm_test"), | ||
{"ETag": "test123", "Last-Modified": "abc123", "some": "header"}, | ||
{"etag": "test123", "last_modified": "abc123"}, | ||
), | ||
], | ||
) | ||
def test_http_source_extract_caching_headers(source, input_data, expected): | ||
"""Test that HTTPSource.extract_caching_headers() works as expected.""" | ||
source.extract_caching_headers(input_data) | ||
assert source.http_etag == expected["etag"] | ||
assert source.http_last_modified == expected["last_modified"] | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"source,expected", | ||
[ | ||
( | ||
HTTPSource(), | ||
{}, | ||
), | ||
( | ||
HTTPSource(http_etag="et_test"), | ||
{"if-none-match": "et_test"}, | ||
), | ||
( | ||
HTTPSource(http_last_modified="lm_test"), | ||
{"if-modified-since": "lm_test"}, | ||
), | ||
( | ||
HTTPSource(http_etag="et_test", http_last_modified="lm_test"), | ||
{"if-modified-since": "lm_test", "if-none-match": "et_test"}, | ||
), | ||
], | ||
) | ||
def test_http_source_make_caching_headers(source, expected): | ||
"""Test that HTTPSource.make_caching_headers() works as expected.""" | ||
result = source.make_caching_headers() | ||
assert result == expected |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.