-
Notifications
You must be signed in to change notification settings - Fork 818
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
rfctr: prepare for adding metadata.orig_elements field #2647
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
## 0.12.7-dev2 | ||
## 0.12.7-dev3 | ||
|
||
### Enhancements | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,42 @@ | ||
"""Utilities that ease unit-testing.""" | ||
|
||
from __future__ import annotations | ||
|
||
import datetime as dt | ||
import difflib | ||
import pathlib | ||
from typing import List, Optional | ||
from typing import Any, List, Optional | ||
from unittest.mock import ( | ||
ANY, | ||
MagicMock, | ||
Mock, | ||
PropertyMock, | ||
call, | ||
create_autospec, | ||
mock_open, | ||
patch, | ||
) | ||
|
||
from pytest import FixtureRequest, LogCaptureFixture # noqa: PT013 | ||
|
||
from unstructured.documents.elements import Element | ||
from unstructured.staging.base import elements_from_json, elements_to_json | ||
|
||
__all__ = ( | ||
"ANY", | ||
"FixtureRequest", | ||
"LogCaptureFixture", | ||
"MagicMock", | ||
"Mock", | ||
"call", | ||
"class_mock", | ||
"function_mock", | ||
"initializer_mock", | ||
"instance_mock", | ||
"method_mock", | ||
"property_mock", | ||
) | ||
|
||
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. Not that this needs to be changed, but isn't 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. Yes, it is, but it also indicates what you are intentionally "exporting" and prevents ruff errors related to "xyz is imported but not used". That's why I have it here. |
||
|
||
def assert_round_trips_through_JSON(elements: List[Element]) -> None: | ||
"""Raises AssertionError if `elements -> JSON -> List[Element] -> JSON` are not equal. | ||
|
@@ -54,3 +83,136 @@ def example_doc_path(file_name: str) -> str: | |
def parse_optional_datetime(datetime_str: Optional[str]) -> Optional[dt.datetime]: | ||
"""Parse `datetime_str` to a datetime.datetime instance or None if `datetime_str` is None.""" | ||
return dt.datetime.fromisoformat(datetime_str) if datetime_str else None | ||
|
||
|
||
# ------------------------------------------------------------------------------------------------ | ||
# MOCKING FIXTURES | ||
# ------------------------------------------------------------------------------------------------ | ||
# These allow full-featured and type-safe mocks to be created simply by adding a unit-test | ||
# fixture. | ||
# ------------------------------------------------------------------------------------------------ | ||
|
||
|
||
def class_mock( | ||
request: FixtureRequest, q_class_name: str, autospec: bool = True, **kwargs: Any | ||
) -> Mock: | ||
"""Return mock patching class with qualified name `q_class_name`. | ||
|
||
The mock is autospec'ed based on the patched class unless the optional argument `autospec` is | ||
set to False. Any other keyword arguments are passed through to Mock(). Patch is reversed after | ||
calling test returns. | ||
""" | ||
_patch = patch(q_class_name, autospec=autospec, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def cls_attr_mock( | ||
request: FixtureRequest, | ||
cls: type, | ||
attr_name: str, | ||
name: str | None = None, | ||
**kwargs: Any, | ||
): | ||
"""Return a mock for attribute `attr_name` on `cls`. | ||
|
||
Patch is reversed after pytest uses it. | ||
""" | ||
name = request.fixturename if name is None else name | ||
_patch = patch.object(cls, attr_name, name=name, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def function_mock( | ||
request: FixtureRequest, q_function_name: str, autospec: bool = True, **kwargs: Any | ||
): | ||
"""Return mock patching function with qualified name `q_function_name`. | ||
|
||
Patch is reversed after calling test returns. | ||
""" | ||
_patch = patch(q_function_name, autospec=autospec, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def initializer_mock(request: FixtureRequest, cls: type, autospec: bool = True, **kwargs: Any): | ||
"""Return mock for __init__() method on `cls`. | ||
|
||
The patch is reversed after pytest uses it. | ||
""" | ||
_patch = patch.object(cls, "__init__", autospec=autospec, return_value=None, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def instance_mock( | ||
request: FixtureRequest, | ||
cls: type, | ||
name: str | None = None, | ||
spec_set: bool = True, | ||
**kwargs: Any, | ||
): | ||
"""Return a mock for an instance of `cls` that draws its spec from the class. | ||
|
||
The mock does not allow new attributes to be set on the instance. If `name` is missing or | ||
|None|, the name of the returned |Mock| instance is set to *request.fixturename*. Additional | ||
keyword arguments are passed through to the Mock() call that creates the mock. | ||
""" | ||
name = name if name is not None else request.fixturename | ||
return create_autospec(cls, _name=name, spec_set=spec_set, instance=True, **kwargs) | ||
|
||
|
||
def loose_mock(request: FixtureRequest, name: str | None = None, **kwargs: Any): | ||
"""Return a "loose" mock, meaning it has no spec to constrain calls on it. | ||
|
||
Additional keyword arguments are passed through to Mock(). If called without a name, it is | ||
assigned the name of the fixture. | ||
""" | ||
if name is None: | ||
name = request.fixturename | ||
return Mock(name=name, **kwargs) | ||
|
||
|
||
def method_mock( | ||
request: FixtureRequest, | ||
cls: type, | ||
method_name: str, | ||
autospec: bool = True, | ||
**kwargs: Any, | ||
): | ||
"""Return mock for method `method_name` on `cls`. | ||
|
||
The patch is reversed after pytest uses it. | ||
""" | ||
_patch = patch.object(cls, method_name, autospec=autospec, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def open_mock(request: FixtureRequest, module_name: str, **kwargs: Any): | ||
"""Return a mock for the builtin `open()` method in `module_name`.""" | ||
target = "%s.open" % module_name | ||
_patch = patch(target, mock_open(), create=True, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def property_mock(request: FixtureRequest, cls: type, prop_name: str, **kwargs: Any) -> Mock: | ||
"""A mock for property `prop_name` on class `cls`. | ||
|
||
Patch is reversed at the end of the test run. | ||
""" | ||
_patch = patch.object(cls, prop_name, new_callable=PropertyMock, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() | ||
|
||
|
||
def var_mock(request: FixtureRequest, q_var_name: str, **kwargs: Any): | ||
"""Return a mock patching the variable with qualified name `q_var_name`. | ||
|
||
Patch is reversed after calling test returns. | ||
""" | ||
_patch = patch(q_var_name, **kwargs) | ||
request.addfinalizer(_patch.stop) | ||
return _patch.start() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = "0.12.7-dev2" # pragma: no cover | ||
__version__ = "0.12.7-dev3" # pragma: no cover |
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.
Does this change need to be replicated in in
.pre-commit-config.yaml
andMakefile
? (note at the top)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.
Hmm, yeah, good call. I've removed those redundant explicit options definitions. Now they should be picked up from the
pyproject.toml
so it's the single source of truth on options.