-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Various fixes and improvements #41
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
37db8ff
feat: Add documentation for `Dataset`, `KeyValueStore`, and `RequestQ…
jirimoravcik 6860933
Merge branch 'master' into feature/fixes-and-improvements
jirimoravcik 72b8883
feat: Various fixes and improvements
jirimoravcik 8fbfccb
simplify env var name gettings
jirimoravcik 4fb6965
address PR comments
jirimoravcik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ | |
from enum import Enum | ||
from typing import Any, Callable, Dict, Generic, ItemsView, Iterator, NoReturn, Optional | ||
from typing import OrderedDict as OrderedDictType | ||
from typing import Type, TypeVar, Union, ValuesView, cast, overload | ||
from typing import Tuple, Type, TypeVar, Union, ValuesView, cast, overload | ||
|
||
import aioshutil | ||
import psutil | ||
|
@@ -285,12 +285,12 @@ def _is_file_or_bytes(value: Any) -> bool: | |
return isinstance(value, (bytes, bytearray, io.IOBase)) | ||
|
||
|
||
def _maybe_parse_body(body: bytes, content_type: str) -> Any: # TODO: Improve return type | ||
def _maybe_parse_body(body: bytes, content_type: str) -> Any: | ||
try: | ||
if _is_content_type_json(content_type): | ||
return json.loads(body) # Returns any | ||
elif _is_content_type_xml(content_type) or _is_content_type_text(content_type): | ||
return body.decode('utf-8') # TODO: Check if utf-8 can be assumed | ||
return body.decode('utf-8') | ||
except ValueError as err: | ||
print('_maybe_parse_body error', err) | ||
return body | ||
|
@@ -361,12 +361,11 @@ def __setitem__(self, key: str, value: T) -> None: | |
|
||
def __delitem__(self, key: str) -> None: | ||
"""Remove an item from the cache.""" | ||
# TODO: maybe do? self._cache.__delitem__(key) | ||
del self._cache[key] | ||
|
||
def __iter__(self) -> Iterator: | ||
def __iter__(self) -> Iterator[str]: | ||
"""Iterate over the keys of the cache in order of insertion.""" | ||
yield from self._cache.__iter__() | ||
return self._cache.__iter__() | ||
|
||
def __len__(self) -> int: | ||
"""Get the number of items in the cache.""" | ||
|
@@ -383,3 +382,38 @@ def items(self) -> ItemsView[str, T]: # Needed so we don't mutate the cache by | |
|
||
def _is_running_in_ipython() -> bool: | ||
return getattr(builtins, '__IPYTHON__', False) | ||
|
||
|
||
@overload | ||
def _budget_ow(value: Union[str, int, float, bool], predicate: Tuple[Type, bool], value_name: str) -> None: # noqa: U100 | ||
... | ||
|
||
|
||
@overload | ||
def _budget_ow(value: Dict, predicate: Dict[str, Tuple[Type, bool]]) -> None: # noqa: U100 | ||
... | ||
|
||
|
||
def _budget_ow( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess, there should be some dynamic type checker in python. But I like the name maybe you should create package of it 😄 |
||
value: Union[Dict, str, int, float, bool], | ||
predicate: Union[Dict[str, Tuple[Type, bool]], Tuple[Type, bool]], | ||
value_name: Optional[str] = None, | ||
) -> None: | ||
"""Budget version of ow.""" | ||
def validate_single(field_value: Any, expected_type: Type, required: bool, name: str) -> None: | ||
if field_value is None and required: | ||
raise ValueError(f'"{name}" is required!') | ||
if (field_value is not None or required) and not isinstance(field_value, expected_type): | ||
raise ValueError(f'"{name}" must be of type "{expected_type.__name__}" but it is "{type(field_value).__name__}"!') | ||
|
||
# Validate object | ||
if isinstance(value, dict) and isinstance(predicate, dict): | ||
for key, (field_type, required) in predicate.items(): | ||
field_value = value.get(key) | ||
validate_single(field_value, field_type, required, key) | ||
# Validate "primitive" | ||
elif isinstance(value, (int, str, float, bool)) and isinstance(predicate, tuple) and value_name is not None: | ||
field_type, required = predicate | ||
validate_single(value, field_type, required, value_name) | ||
else: | ||
raise ValueError('Wrong input!') |
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
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,54 @@ | ||
import pytest | ||
|
||
from apify import Actor | ||
from apify.config import Configuration | ||
from apify.consts import ApifyEnvVars | ||
from apify.memory_storage import MemoryStorage | ||
from apify.storage_client_manager import StorageClientManager | ||
from apify.storages import StorageManager | ||
|
||
|
||
@pytest.mark.parametrize('purge_on_start', [True, False]) | ||
async def test_actor_memory_storage_e2e(monkeypatch: pytest.MonkeyPatch, tmp_path: str, purge_on_start: bool) -> None: | ||
"""This test simulates two clean runs using memory storage. | ||
The second run attempts to access data created by the first one. | ||
We run 2 configurations with different `purge_on_start`.""" | ||
# Configure purging env var | ||
monkeypatch.setenv(ApifyEnvVars.PURGE_ON_START, 'true' if purge_on_start else 'false') | ||
# Store old storage client so we have the object reference for comparison | ||
old_client = StorageClientManager.get_storage_client() | ||
async with Actor: | ||
old_default_kvs = await Actor.open_key_value_store() | ||
old_non_default_kvs = await Actor.open_key_value_store('non-default') | ||
# Create data in default and non-default key-value store | ||
await old_default_kvs.set_value('test', 'default value') | ||
await old_non_default_kvs.set_value('test', 'non-default value') | ||
|
||
# Clean up singletons and mock a new memory storage | ||
monkeypatch.setattr(Actor, '_default_instance', None) | ||
monkeypatch.setattr(Configuration, '_default_instance', None) | ||
monkeypatch.setattr(StorageManager, '_default_instance', None) | ||
monkeypatch.setattr(StorageClientManager, '_default_instance', None) | ||
|
||
new_patched_memory_storage = MemoryStorage(local_data_directory=tmp_path) | ||
|
||
def get_storage_client() -> 'MemoryStorage': | ||
return new_patched_memory_storage | ||
monkeypatch.setattr(StorageClientManager, 'get_storage_client', get_storage_client) | ||
|
||
# We simulate another clean run, we expect the memory storage to read from the local data directory | ||
# Default storages are purged based on purge_on_start parameter. | ||
async with Actor: | ||
# Check if we're using a different memory storage instance | ||
assert old_client is not StorageClientManager.get_storage_client() | ||
default_kvs = await Actor.open_key_value_store() | ||
assert default_kvs is not old_default_kvs | ||
non_default_kvs = await Actor.open_key_value_store('non-default') | ||
assert non_default_kvs is not old_non_default_kvs | ||
default_value = await default_kvs.get_value('test') | ||
non_default_value = await non_default_kvs.get_value('test') | ||
if purge_on_start: | ||
assert default_value is None | ||
else: | ||
assert default_value == 'default value' | ||
assert non_default_value == 'non-default value' |
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄 😄 great name